<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">
    <title>Learn Vue</title>

    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>

    <link rel="stylesheet"
          href="./style/style.css">
    <link rel="stylesheet"
          href="./style/vue.css">
</head>

<body data-spy="scroll"
      data-target="#scrollspyer"
      style="position: relative;"
      data-offset="50">

    <!-- <body> -->
    <nav class="navbar navbar-default navbar-fixed-top">
        <div class="navbar-section-header">
            <button class="navbar-toggle pull-left">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a href="#"
               class="navbar-brand">Learn Vue</a>
        </div>
    </nav>


    <div class="container-fliud">
        <div class="side"
             id="scrollspyer">
            <ul class="nav nav-stacked">
                <li class="section-header">基础</li>
                <li class="section active">
                    <a href="#11">Vue实例</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#111">创建一个Vue实例</a></li>
                        <li class="subsection"><a href="#112">数据与方法</a></li>
                        <li class="subsection"><a href="#113">实例生命周期钩子</a></li>
                        <li class="subsection"><a href="#114">生命周期图示</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#12">模板语法</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#121">插值</a></li>
                        <li class="subsection"><a href="#122">指令</a></li>
                        <li class="subsection"><a href="#123">缩写</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#13">计算属性和侦听器</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#131">计算属性</a></li>
                        <li class="subsection"><a href="#132">侦听器</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#14">Class与Style绑定</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#141">绑定HTML Class</a></li>
                        <li class="subsection"><a href="#142">绑定内联样式</a></li>
                    </ul>

                </li>
                <li class="section">
                    <a href="#15">条件渲染</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#151">v-if</a></li>
                        <li class="subsection"><a href="#152">v-show</a></li>
                        <li class="subsection"><a href="#153">v-if vs v-show</a></li>
                        <li class="subsection"><a href="#154">v-if 与 v-for</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#16">列表渲染</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#161">把数组对应为一组元素</a></li>
                        <li class="subsection"><a href="#162">v-for内使用对象</a></li>
                        <li class="subsection"><a href="#163">维护状态</a></li>
                        <li class="subsection"><a href="#164">数组更新检测</a></li>
                        <li class="subsection"><a href="#165">对象变更检测注意事项</a></li>
                        <li class="subsection"><a href="#166">显示过滤/排序后结果</a></li>
                        <li class="subsection"><a href="#167">在v-for里使用值范围</a></li>
                        <li class="subsection"><a href="#168">在template元素上使用v-for</a></li>
                        <li class="subsection"><a href="#169">v-for 和 v-if一起使用</a></li>
                        <li class="subsection"><a href="#1610">在组件上使用v-for</a></li>
                        <li class="subsection"></li>
                        <li class="subsection"></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#17">事件处理</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#171">监听事件</a></li>
                        <li class="subsection"><a href="#172">事件处理方法</a></li>
                        <li class="subsection"><a href="#173">内联事件处理方法</a></li>
                        <li class="subsection"><a href="#174">事件修饰符</a></li>
                        <li class="subsection"><a href="#175">按键修饰符</a></li>
                        <li class="subsection"><a href="#176">系统修饰符</a></li>
                        <li class="subsection"><a href="#177">为何在HTML中监听事件</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#18">表单输入绑定</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#181">基本用法</a></li>
                        <li class="subsection"><a href="#182">值绑定</a></li>
                        <li class="subsection"><a href="#183">修饰符</a></li>
                        <li class="subsection"><a href="#184">在组件上使用v-model</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#19">组件基础</a>
                    <ul class="nav stacked">
                        <li class="subsection"><a href="#191">基本示例</a></li>
                        <li class="subsection"><a href="#192">组件复用</a></li>
                        <li class="subsection"><a href="#193">组件组织</a></li>
                        <li class="subsection"><a href="#194">通过Prop向子组件传递数据</a></li>
                        <li class="subsection"><a href="#195">单个根元素</a></li>
                        <li class="subsection"><a href="#196">监听子组件事件</a></li>
                        <li class="subsection"><a href="#197">通过插槽分发内容</a></li>
                        <li class="subsection"><a href="#198">动态组件</a></li>
                        <li class="subsection"><a href="#199">解析DOM模板时的注意事项</a></li>
                    </ul>
                </li>

                <li class="section-header">深入了解组件</li>
                <li class="section">
                    <a href="#21">组件注册</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#211">组件名</a></li>
                        <li class="subsection"><a href="#212">全局注册</a></li>
                        <li class="subsection"><a href="#213">局部注册</a></li>
                        <li class="subsection"><a href="#214">模块系统</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#22">Prop</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#221">Prop的大小写</a></li>
                        <li class="subsection"><a href="#222">Prop类型</a></li>
                        <li class="subsection"><a href="#223">传递静态或动态Prop</a></li>
                        <li class="subsection"><a href="#224">单向数据流</a></li>
                        <li class="subsection"><a href="#225">Prop验证</a></li>
                        <li class="subsection"><a href="#226">非Prop的Attribute</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#23">自定义事件</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#231">事件名</a></li>
                        <li class="subsection"><a href="#232">自定义组件的v-model</a></li>
                        <li class="subsection"><a href="#233">将原生事件绑定到组件</a></li>
                        <li class="subsection"><a href="#234">.sync修饰符</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#24">插槽</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#241">插槽内容</a></li>
                        <li class="subsection"><a href="#242">编译作用域</a></li>
                        <li class="subsection"><a href="#243">后备内容</a></li>
                        <li class="subsection"><a href="#244">具名插槽</a></li>
                        <li class="subsection"><a href="#245">作用域插槽</a></li>
                        <li class="subsection"><a href="#246">动态插槽名</a></li>
                        <li class="subsection"><a href="#247">具名插槽的缩写</a></li>
                        <li class="subsection"><a href="#248">其他</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#25">动态组件&异步组件</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection">
                            <a href="#251">在动态组件上使用keep-alive</a>
                        </li>
                        <li class="subsection">
                            <a href="#252">异步组件</a>
                        </li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#26">处理边界情况</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection">
                            <a href="#261">访问元素&组件</a>
                        </li>
                        <li class="subsection">
                            <a href="#262">程序化的事件监听器</a>
                        </li>
                        <li class="subsection">
                            <a href="#263">循环引用</a>
                        </li>
                        <li class="subsection">
                            <a href="#264">模板定义的替代品</a>
                        </li>
                        <li class="subsection">
                            <a href="#265">控制更新</a>
                        </li>
                    </ul>
                </li>

                <li class="section-header">过渡&动画</li>
                <li class="section">
                    <a href="#31">过渡&动画</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#311">概述</a></li>
                        <li class="subsection"><a href="#312">单元素/组件的过渡</a></li>
                        <li class="subsection"><a href="#313">初始渲染过渡</a></li>
                        <li class="subsection"><a href="#314">多个元素的过渡</a></li>
                        <li class="subsection"><a href="#315">多个组件的过渡</a></li>
                        <li class="subsection"><a href="#316">列表过渡</a></li>
                        <li class="subsection"><a href="#317">可复用的过渡</a></li>
                        <li class="subsection"><a href="#318">动态过渡</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#32">状态过渡</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#321">状态动画和侦听器</a></li>
                        <li class="subsection"><a href="#322">动态状态过渡</a></li>
                        <li class="subsection"><a href="#323">把过渡放到组件里</a></li>
                    </ul>
                </li>

                <li class="section-header">可复用性&组合</li>
                <li class="section">
                    <a href="#41">混入(Mixin)</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#411">基础</a></li>
                        <li class="subsection"><a href="#412">选项合并</a></li>
                        <li class="subsection"><a href="#413">全局混入</a></li>
                        <li class="subsection"><a href="#414">自定义选项合并策略</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#42">自定义指令</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#421">简介</a></li>
                        <li class="subsection"><a href="#422">钩子函数</a></li>
                        <li class="subsection"><a href="#423">钩子函数参数</a></li>
                        <li class="subsection"><a href="#424">函数简写</a></li>
                        <li class="subsection"><a href="#425">对象字面量</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#43">渲染函数&JSX</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#431">基础</a></li>
                        <li class="subsection"><a href="#432">节点, 树以及虚拟DOM</a></li>
                        <li class="subsection"><a href="#433">createElement参数</a></li>
                        <li class="subsection"><a href="#434">使用js代替模板功能</a></li>
                        <li class="subsection"><a href="#435">JSX</a></li>
                        <li class="subsection"><a href="#436">函数式组件</a></li>
                        <li class="subsection"><a href="#437">模板编译</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#44">插件</a>
                    <ul class="nav nav-stacked">
                        <li class="subsection"><a href="#441">使用插件</a></li>
                        <li class="subsection"><a href="#442">开发插件</a></li>
                    </ul>
                </li>
                <li class="section">
                    <a href="#45">过滤器</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>

                <li class="section-header">工具</li>
                <li class="section">
                    <a href="#51">单文件组件</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#52">单元测试</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#53">TypeScript支持</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#54">生产环境部署</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>

                <li class="section-header">规模化</li>
                <li class="section">
                    <a href="#61">路由</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#62">状态管理</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#63">服务端渲染</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
                <li class="section">
                    <a href="#64">安全</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>

                <li class="section-header">内在</li>
                <li class="section">
                    <a href="#71">深入响应式原理</a>
                    <ul class="nav nav-stacked">

                    </ul>
                </li>
            </ul>
        </div>

        <div class="content">
            <section>
                <h1 id="11">Vue实例</h1>

                <article>
                    <h2 id="111">创建一个Vue实例</h2>
                    <div>
                        <p>
                            每个Vue应用通过<code>Vue</code>函数创建一个新实例开始, 虽然没有完全遵循<strong>MVVM</strong>模型, 但是Vue的设计也受到了它的启发.
                            因此文档中经常会用
                            <code><abbr title="ViewModel">vm</abbr></code>这个变量名表示Vue实例.
                        </p>
                        <p>
                            当创建一个Vue实例时可传入一个<strong>options</strong>对象.
                            <code>options</code>对象的每个property可以称作为一个选项(option).
                        </p>
                        <p>
                            一个Vue应用由一个通过<code>new Vue</code>创建的<strong>根Vue实例</strong>, 以及可选的嵌套的、 可复用的组件树数组成.
                        </p>
                        <p>
                            所有的Vue组件都是Vue实例.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="112">数据与方法</h2>

                    <div>
                        <p>
                            当一个Vue实例被创建, 它将<code>data</code>对象中的所有属性加入到Vue的<strong>响应式系统</strong>中.
                            当这些属性值发生改变, 视图将产生"响应", 例外是用Object.freeze冻结data对象.
                        </p>
                        <p>
                            当这些数据被改变, 视图将重新渲染. 注意只有实例创建时就存在与<code>data</code>中的属性才是<strong>响应式</strong>的.
                            即如果添加一个属性, 如<code>vm.foo = "bar"</code>那么对<code>foo</code>的改动将不会触发任何视图的更新.
                        </p>
                        <p>
                            除了数据属性, Vue实例还暴露了一些有用的实例属性和方法, 它们都有前缀<code>$</code>, 以便区别与用户自定义的属性区分开.
                            例如 :
                        </p>
                        <pre>let data = {a: 1};<br>let vm = new Vue({el: "example", data: data});<br>vm.$data === data; // true</pre>

                        <p>
                            Vue的api可以这样分类 :
                        </p>
                        <ul>
                            <li>
                                全局可以分为以下两类 :
                                <ol>
                                    <li>全局配置(global config), 通过<code>Vue.config</code>调用</li>
                                    <li>全局api, 通过<code>Vue</code>调用</li>
                                </ol>
                            </li>
                            <li>Options api, 描述options对象中可以有哪些options, 可分为以下几类
                                <ol>
                                    <li>选项/数据</li>
                                    <li>选项/DOM</li>
                                    <li>选项/生命周期钩子</li>
                                    <li>选项/资源</li>
                                    <li>选项/组合</li>
                                    <li>选项/其他</li>
                                </ol>
                            </li>
                            <li>实例api, 描述了Vue实例的property, 这些property通常以 <code>$</code>作为前缀, 可分为以下几类
                                <ol>
                                    <li>
                                        实例属性
                                    </li>
                                    <li>
                                        实例方法/数据
                                    </li>
                                    <li>
                                        实例方法/事件
                                    </li>
                                    <li>实例方法/声明周期</li>
                                </ol>
                            </li>
                            <li>指令</li>
                            <li>特殊attribute</li>
                            <li>内置组件</li>
                            <li>VNode接口</li>
                            <li>服务端渲染</li>
                        </ul>

                        <br>

                        <p>
                            <code>$options</code>属于实例属性, 帮助实例<code>vm</code>储存options对象
                        </p>

                        <br>
                        <br>

                        <p>
                            <code>el</code>属于选项/DOM, 类型<code>string | Element</code>, 限制在只有用<code>new</code>创建Vue实例时才生效.
                            它提供一个页面上已经存在的DOM元素作为Vue实例的"挂载(mount)"目标, 可以是一个用字符串表示的CSS选择器, 也可以是一个HTMLElement实例.
                        </p>
                        <p>
                            如果实例化时存在<code>el</code> option, 实例立刻进入编译过程, 否则, 需要显式调用 <code>vm.$mount</code>手动开启编译
                        </p>
                        <p>
                            <code>$el</code>属于实例属性, 类型为 <code>Element</code>, 只读, 作为Vue实例使用的根DOM元素.
                            在Vue实例在挂载之后, 作为挂载目标的元素可以通过可以通过<code>vm.$el</code>访问.
                        </p>
                        <div class="text-warning bg-warning">
                            提供的元素只能作为挂载点. 不同于 Vue 1.x, 在Vue2.x中所有的挂载元素会被Vue生成的DOM替换. 因此不推荐挂载root实例到
                            <code>html</code>或<code>body</code>元素上.
                        </div>
                        <br>
                        <div class="bg-warning text-warning">
                            如果options中<code>render</code>和<code>template</code>选项都不存在, 挂载DOM元素的HTML会被提取出来当作模板, 此时, 必须使用
                            <a
                               href="https://cn.vuejs.org/v2/guide/installation.html#%E8%BF%90%E8%A1%8C%E6%97%B6-%E7%BC%96%E8%AF%91%E5%99%A8-vs-%E5%8F%AA%E5%8C%85%E5%90%AB%E8%BF%90%E8%A1%8C%E6%97%B6">Runtime
                                + Compiler</a>构建的Vue库.
                        </div>
                        <div class="text-warning bg-warning">

                        </div>

                        <br>
                        <br>

                        <p>
                            <code>data</code> option属于选项/数据, 类型 <code>Object | Function</code>. 作为Vue实例的数据对象.
                            Vue会递归将<code>data</code>中的属性转换为getter / setter, 从而让<code>data</code>的属性能够响应数据变化.
                            <strong>对象必须是纯粹的对象(包含0 / 多个 key/value 对)</strong> ： 浏览器api创建的原生对象, 其prototype上的property会被忽略.
                            大概来说, data应该只能是数据 - 不推荐观察拥有状态行为的对象.
                        </p>

                        <p>
                            <code>$data</code>属于实例属性, 类型 <code>Object</code>, 作为Vue实例观察的数据对象. 实例创建之后,
                            就可以通过<code>vm.$data</code>访问原始数据对象<code>data</code>.
                            Vue实例也代理了data对象上的所有属性, 因此访问 <code>vm.foo</code>等价于访问 <code>vm.$data.foo</code>.
                        </p>

                        <p>
                            <code>data</code>中 以<code>_</code> 或 <code>$</code>开头的property <strong>不会</strong>被Vue实例代理,
                            因为它们可能和Vue内置的属性, api方法冲突. 这种情况下只能通过<code>vm.$data</code>访问这些属性
                        </p>

                        <p>
                            如果为<code>data</code>选项使用了箭头函数, this不会指向<code>vm</code>实例, 但仍可通过提供第一个参数作为<code>vm</code>使用
                        </p>
                    </div>
                </article>


                <article>
                    <h2 id="113">实例生命周期钩子</h2>

                    <div>
                        <p>
                            每个Vue实例在被创建时都要经过一系列初始化——例如, 设置数据监听, 编译模板, 将vue实例挂载到DOM并在数据变化时更新DOM等.
                            同时, 这个过程也会运行一些叫<strong>生命周期钩子</strong>的函数, 给予用户在不同阶段添加自己代码的机会.
                        </p>
                        <p>
                            如 <strong>created</strong>, 属于选项/生命周期钩子, 类型<code>Function</code>
                        </p>
                        <p>
                            在实例创建完成后立刻调用. 在这一步, 实例已完成以下配置: 1. 数据观测(data observer), 属性和方法的运算, watch/event事件回调.
                            然而挂载阶段还没开始, <code>$el</code>属性尚不可用.
                        </p>

                        <p>
                            在Vue中, 所有options对象内的非箭头函数方法中this 指向创建出来的Vue实例<code>vm</code>
                        </p>

                        <pre>
let options = {
    el: "#hookThisDemo",
    created: function () {
        console.log(this === options); // this指向vm, 所以结果为false
    }
}
let hookThisDemo = new Vue(options);
                        </pre>

                        <pre>
&lt;div id="hookThisDemo"&gt;
&lt;/div&gt;
                        </pre>

                        <div id="hookThisDemo">
                        </div>

                        <p>
                            简单回顾js <code>this</code> 的指向.
                        </p>
                        <ul>
                            <li>当this在全局上下文中时(global context, 即不在任何function 中) : this 指向 <code>globalThis</code></li>
                            <li>当this在函数上下文时, <code>this</code> 的指向取决于外部函数的类型和调用方式 :
                                <ol>
                                    <li>
                                        普通调用, 指向<code>globalThis</code>
                                    </li>
                                    <li>
                                        函数通过call 或 apply方法 调用, this 指向方法的第一个参数
                                    </li>
                                    <li>通过bind方法调用, this 永远指向第一次调用bind时的第一个参数</li>
                                    <li>如果函数是箭头函数, 则this永远指向它的闭合文法上下文的this(enclosing lexical context's this),
                                        简单来说就是永远指向定义该箭头函数的最直接的代码块的this</li>
                                    <li>当函数被用作构造器时, this指向构造出来的对象</li>
                                    <li>当函数是对象方法时, this指向对象</li>
                                    <li>当函数是DOM事件处理器时, this指向该处理器监听的元素</li>
                                    <li>当函数作为内联事件处理器 (...很少用到)</li>
                                </ol>
                            </li>
                        </ul>

                        <div class="alert-warning">
                            <p>不要在选项属性(即vm中以$开头的属性)或回调上使用箭头函数. 例如 : </p>
                        </div>

                        <pre>
new Vue({
    data: {
        foo: 'bar'
    },
    created: () => console.log(this.foo);
})
                        </pre>

                        <p>
                            这里因为该箭头函数的闭合文法上下文为Vue构造器的第一个参数即options对象, 所以该箭头函数的this永远指向选项对象的this, 而选项对象的this定义在全局上下文,
                            所以箭头函数的this永远指向globalThis.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="114">生命周期示意图</h2>
                    <div>
                        <img src="images\lifecycle.png"
                             alt=""
                             class="img-responsive">
                    </div>
                </article>
            </section>

            <section>
                <h1 id="12">模板语法</h1>

                <article>
                    <p>
                        Vue.js使用了基于HTML的模板语法, 允许开发者声明式地将DOM绑定至底层地Vue实例数据. 所有Vue.js 的模板都是合法的HTML, 所以能被遵循规范的浏览器和HTML解析器解析.
                    </p>

                    <p>
                        在底层实现上, Vue将模板编译成虚拟DOM渲染函数. 结合响应式系统, Vue能够智能计算出最少需要重新渲染多少组件, 并把DOM操作数减到最少.
                        熟悉虚拟DOM并偏爱JS原始力量的开发者可以直接写渲染函数, 使用可选的JSX语法.
                    </p>
                </article>

                <article>
                    <h2 id="121">插值</h2>

                    <div>
                        <h4>文本插值</h4>
                        <div>
                            <p>数据绑定最常见形式为使用 "Mustache"语法 (双括号) 的文本插值</p>
                            <p>这种双括号语法被称作 <strong>expression</strong>, 在双括号里我们可以运行任何js表达式,
                                表达式内我们可以直接访问实例<code>vm</code>的property</p>

                            <pre>&lt;div id="app-1"&gt;{{msg}}&lt;/div&gt;</pre>
                            <pre>
let app1 = new Vue({
    el: "#app1", 
    data: {
        msg: "Hello, Vue"
    }
});         
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-1">{{msg}}</div>

                                </samp>
                            </div>

                            <p>
                                <code>el</code>属性的值是一个选择器字符串用于选中DOM元素, <code>data</code>用于创建响应式属性.
                                Mustache 标签将会被替代为对应数据对象上<code>msg</code>属性的值. 无论何时，绑定的数据对象上 msg 属性发生了改变，插值处的内容都会更新.
                            </p>

                            <p>
                                可以通过使用<code>v-once</code>指令可以进行一次性插值
                            </p>

                            <p>
                                我们还可以使用指令<code>v-text</code>来替换双括号语法, 例如:
                            </p>

                            <pre>&lt;p id="v-text-demo" v-text="msg"&gt;&lt;/p&gt;</pre>
                            <pre>
let vTextDemo = new Vue({
    el: "#v-text-demo",
    data: {
        msg: "v-text demo"
    }
})
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <p id="v-text-demo"
                                       v-text="msg"></p>
                                </samp>
                            </div>

                            <p>以上等价于 <code>&lt;p&gt;{{ msg }}&lt;/p&gt;</code>.</p>
                            <p>
                                该指令更新元素的<code>textContent</code>, 如果只是想要更新元素部分的<code>textContent</code>, 应该使用双括号语法进行插值.
                            </p>

                            <br>

                            <p>
                                当想要显示原始的双括号时, 可以使用<code>v-pre</code>指令, 该指令不需要表达式,
                                它会使元素和它的子元素跳过编译过程. 例如:
                            </p>

                            <pre>
&lt;p id="v-pre-demo" v-pre&gt;{{ msg }} will not be compiled&lt;/p&gt;
                            </pre>

                            <pre>
let vPreDemo = new Vue({
    el: "#v-pre-demo",
    data: {
        msg: "v-pre demo"
    }
})    
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <p id="v-pre-demo"
                                       v-pre>{{ msg }} will not be compiled</p>
                                </samp>
                            </div>


                            <p>
                                跳过大量没有指令的节点会加快编译.
                            </p>

                            <br>

                            <p>
                                有时候编译过程较慢可能会在页面上看见双括号未被编译的原始样子. 为了解决这个问题,
                                可以使用<code>v-cloak</code>指令, 这个指令不需要表达式, 它保持在元素上直到关联的实例结束编译.
                                配合css规则如 <code>[v-cloak] { display: none }</code>一起使用可以隐藏未编译的mustache标签直到实例准备完毕.
                            </p>

                            <pre>
&lt;p id="no-v-cloak-demo"&gt;{{ msg00000000 }}&lt;/p&gt;

&lt;p id="with-v-cloak-demo" v-cloak&gt;{{ msg11111111 }}&lt;/p&gt;
                            </pre>

                            <pre>
let noVCloakDemo = new Vue({
    el: "#no-v-cloak-demo",
    data: {
        msg00000000: "without v-cloack"
    }
})

let withVCloakDemo = new Vue({
    el: "#with-v-cloak-demo",
    data: {
        msg11111111: "with v-cloack"
    }
})
                            </pre>

                            <pre>
[v-cloak] {
    display: none;
}
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <p id="no-v-cloak-demo">{{ msg00000000 }}</p>

                                    <p id="with-v-cloak-demo"
                                       v-cloak> {{ msg11111111 }} </p>
                                </samp>
                            </div>

                            <p>
                                快速刷新页面我们就能够看见两者的区别
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>原始HTML</h4>
                        <div>
                            <p>双大括号会见数据解释为普通文本, 而非html代码. 为了输出真正的HTML, 需要用<code>v-html</code>指令</p>
                            <p>
                                指令更新元素的<code>innerHTML</code>
                            </p>

                            <pre>&lt;p id="app-2"&gt;Using mustaches: {{rawHtml}}&lt;/p&gt;</pre>
                            <pre>
let app2 = new Vue({
    el: "#app-2",
    data: {
        rawHtml: '&lt;div style="color": red;&gt;Red&lt;/div&gt;'
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-2">Using mustaches: {{rawHtml}}</div>
                                </samp>
                            </div>

                            <pre>&lt;p id="app-3"&gt;Using v-html: &lt;span v-html="rawHtml"&gt;&lt;/span&gt; &lt;/p&gt;</pre>

                            <pre>
let app3 = new Vue({
    el: "#app-3",
    data: {
        rawHtml: '&lt;div style="color": red;&gt;Red&lt;/div&gt;'
    }
});
                            </pre>

                            <p>
                                <code>span</code>的内容会被替换为<code>rawHtml</code>里的值, 且直接作为HTML渲染.
                            </p>

                            <div class="code-output">
                                <samp>
                                    <div id="app-3">Using v-html: <span v-html="rawHtml"></span></div>
                                </samp>
                            </div>

                            <div class="danger-text bg-danger">
                                在站点上动态渲染任意HTML非常危险, 因为很容易导致XSS攻击. 只对可信内容使用HTML插值, <strong>绝不要</strong>对用户提供的内容插值
                            </div>

                        </div>
                    </div>

                    <div>
                        <h4>Attribute</h4>
                        <div>
                            <p>
                                双括号语法不能用于HTML attribute上, 应使用<code>v-bind</code>指令 :
                            </p>

                            <pre>
&lt;div id="app-4"&gt;
    &lt;a v-bind:href="url"&gt;link&lt;/a&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let app4 = new Vue({
    el: "#app-4",
    data: {
        url: "https://cn.vuejs.org/"
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-4"><a v-bind:href="url">link</a></div>
                                </samp>
                            </div>

                            <p>对于布尔attribute, 若值为假则该属性不会被包含在渲染出的元素中</p>
                        </div>
                    </div>

                    <div>
                        <h4>使用js表达式</h4>
                        <div>
                            <p>插值还可以是一个js表达式, 例如 : </p>
                            <pre>&lt;div id="app5"&gt;{{number ** 2}}&lt;/div&gt;</pre>
                            <pre>let app5 = new Vue({el: "#app5", data: {number: 2}});</pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-5">{{number ** 2}}</div>
                                </samp>
                            </div>
                        </div>
                    </div>
                </article>


                <article>
                    <h2 id="122">指令</h2>

                    <div>
                        <p>
                            指令(Directives)是带有<code>v-</code>前缀的特殊attribute. 指令attribute的值预期是 <strong>单个js表达式</strong> (
                            <code>v-for</code> 例外).

                            <p>从方便理解的角度, 我们可以将一个 <code>v-xxx="expression"</code> 这样的指令看成
                                <code>v-xxx={{ expression }}</code>
                            </p>
                    </div>

                    <div>
                        <h4>参数</h4>
                        <div>
                            <p> 一个指令能接受一个参数, 参数跟在指令后面用冒号隔开. 例如 <code>v-bind:href</code>可响应式更新HTML
                                attribute.</p>
                        </div>
                    </div>

                    <div>
                        <h4>动态参数</h4>
                        <div>
                            <p>
                                从2.6.0开始, 可以用方括号括起来的js表达式作为指令的参数, 该表达式预期会求出一个字符串, 异常情况值为<code>null</code>.
                                任何其他非字符串类型的值都会触发警告.
                            </p>
                            <p>
                                注意动态参数值的约束, 某些字符如空格和引号放在HTML的attribute名是无效的 :
                            </p>
                            <pre>&lt;a v-bind:['foo' + bar]="value&gt;...&lt;/a&gt;</pre>
                            <p>
                                在DOM中使用模板(直接在HTML文件里写模板), 还需要避免使用大写字符命名attribute, 因为浏览器会把attribute名全部强制转化为小写,
                                即使式动态参数中的标识符 :
                            </p>

                            <pre>
&lt;!-- v-bind[someAttr]会被转换为v-bind:[someattr], 
意味着实例中应该有一个 名为"someattr"的property而不是"someAttr", 
否则代码无法工作 -- &gt;

&lt; v-bind:[someAttr]="value"&gt;...&lt;/a&gt;
                            </pre>

                            <p>
                                因为浏览器本身并不知道什么是Vue指令, 所以整个 <code>v-bind[someAttr]</code>被当作一个attribute并强制转换成小写
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>修饰符</h4>
                        <div>
                            <p>
                                修饰符(modifier)是以半角句号<code>.</code>指明的特殊后缀, 用于指出一个指令应该以特殊方式绑定
                            </p>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="123">缩写</h2>

                    <div>
                        <p>
                            Vue为<code>v-bind</code>和<code>v-on</code>这两个最常用的指令, 提供了特定缩写:
                            <code>v-bind:</code>对应<code>:</code>,
                            <code>v-on:</code>对应<code>@</code>
                        </p>

                        <p>
                            要注意, 缩写仅在指令有参数的情况下才能使用
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="13">计算属性和侦听器</h1>

                <article>
                    <h2 id="131">计算属性</h2>

                    <div>
                        <p>
                            模板内的表达式非常便利, 但是在模板中放入过多逻辑会让模板过重且难以维护. Vue在<code>options</code>对象中提供了<code>computed</code>
                            property,
                            以便让我们能够将复杂的计算逻辑放入该对象的对应计算属性中.
                        </p>
                        <pre>&lt;div id="app6"&gt;<br>  Original message: {{message}}, Computed reversed message: {{reversedMessage}} <br>&lt;/div&gt;</pre>
                        <pre>
let app6 = new Vue({
    el: "#app6", 
    data: {
        message: "Vue"
    }, 
    computed: {
        reversedMessage: function() { 
            return this.message
                        .split("")
                        .reverse()
                        .join("");
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-6">
                                    Original message: {{message}}, Computed reversed message: {{reversedMessage}}
                                </div>
                            </samp>
                        </div>

                        <p>
                            这里声明了一个计算属性<code>reversedMessage</code>, 我们提供的函数将作为属性<code>vm.reversedMesssage</code>的
                            getter函数.
                            注意此处的<code>this</code>指向<code>app6</code>
                        </p>

                        <br>

                        <p>
                            <code>computed</code>属于选项/数据, 储存了我们定义的计算属性. <code>computed</code>
                            类型为<code>{ [key: string]: Function | {get: Function, set: Function} }</code>, 即一个对象,
                            里面包含任意数量的键值对, 称作计算属性,
                            键为字符串或任何结果为字符串的表达式(方括号括起时作为动态键值)作为计算属性的名称,
                            值为函数或一个定义了get和set的对象, 值为函数时会被转换为getter
                        </p>
                        <p>
                            计算属性被混入Vue实例中, 其所有的getter和setter的<code>this</code>上下文自动绑定到Vue实例.
                            不同于<code>data</code>, 计算属性即使以<code>$</code>或<code>_</code>开头也依然会被混入Vue实例中 :
                        </p>

                        <pre>
let app = new Vue({
    data: {
        $foo: 2
    },
    computed: {
        $bar : function () {
            // 必须通过 $data 访问 $foo
            return this.$data.$foo * 2;
        }
    }
});

app.$foo; // => undefined
app.$bar; // => 4
                        </pre>

                        <p>
                            总之, 应该避免使用<code>$</code>和<code>_</code>前缀
                        </p>


                        <p class="text-info bg-info">
                            注意, 就如生命周期钩子一样, 如果使用箭头函数作为计算属性的值, this不会指向Vue实例, 但是于生命周期钩子不同的是,
                            我们可以为函数提供一个参数来获取Vue实例.
                        </p>
                    </div>

                    <div>
                        <h4>计算属性缓存vs方法</h4>
                        <div>
                            <p>使用方法可以达到同样的效果</p>
                            <pre>
&lt;div id="app7"&gt;
    Original message: {{message}}, Computed reversed message: {{reversedMessage()}}
&lt;/div&gt;
                            </pre>

                            <pre>
let app7 = new Vue({
    el: "#app7", 
    data: {
        message: "Vue"
    }, 
    methods: {
        reversedMessage: function() { 
            return this.message
                        .split("")
                        .reversed()
                        .join("");
        }
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-7">
                                        Original message: {{message}}, Computed reversed message: {{reversedMessage()}}
                                    </div>
                                </samp>
                            </div>


                            <p>
                                两种方式结果相同, 但计算属性是基于它们的响应式依赖进行缓存的. 只有在相关响应式依赖发生变化时, 它才会重新求值,
                                这就意味这只要<code>message</code>不变,
                                那么多次访问<code>reversedMessage</code>计算属性会立刻返回之前的计算结果, 而不必再次执行函数.
                            </p>

                            <br>
                            <br>

                            <p>
                                <code>methods</code>属于选项/数据, 类型为 : <code>{ [key: string]: Function }</code>
                            </p>
                            <p>
                                这个选项定义了实例的方法, <code>methods</code>选项中的所有方法会被混入到Vue实例中. 可以通过Vue实例直接访问这些方法,
                                或者在指令表达式, 双括号插值中使用. 方法中的this自动绑定为Vue实例. 方法不会像计算属性一样被转换为getter, setter,
                                必须通过<code>()</code>才能调用方法.
                            </p>
                            <p class="text-warning bg-warning">
                                注意不要用箭头函数定义method
                            </p>

                            <p>
                                到目前位置, 我们已经知道了<code>data</code>, <code>computed</code>和 <code>methods</code> 中的属性
                                会被混入Vue对象, 所以我们应该注意它们之间的命名冲突.
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>计算属性<code>computed</code>vs侦听属性<code>watch</code></h4>
                        <p>
                            Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动：侦听属性。当你有一些数据需要随着其它数据变动而变动时，你很容易滥用 watch——特别是如果你之前使用过
                            AngularJS。然而，通常更好的做法是使用计算属性而不是命令式的 watch 回调。
                        </p>




                        <div>
                            <h4>计算属性的setter</h4>
                            <div>
                                <p>
                                    当计算属性值为一个函数时该函数被默认为getter, 当需要设置setter时, 给计算属性赋值为一个含有getter和setter的对象, 例如 :
                                </p>
                                <pre>computed: {cp: {get: function() {}, set: function (newValue) {} }}</pre>
                            </div>
                        </div>
                </article>

                <article>
                    <h2 id="132">侦听器</h2>

                    <div>
                        <p>
                            大多数情况下, 使用计算属性更合适, 但侦听器更加通用. 在需要数据变化时执行异步或开销较大时, 这个方式最有用
                        </p>

                        <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
                        <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>

                        <div class="code-output">
                            <samp>
                                <div id="app-8">
                                    <p>Ask a yes / no question :</p>
                                    <input type="text"
                                           v-model="question">
                                    <p>{{answer}}</p>
                                </div>
                            </samp>
                        </div>


                        <div>
                            <p>
                                Vue提供了更通用的方式来观察和响应Vue实例上的数据变动: <strong>侦听属性</strong> <code>watch</code>, 属于选项/数据,
                                类型为<code>{ [key: string]: string | Function | Object | Array }</code>, 即一个对象, 包含若干键值对,
                                键为需要观察的表达式, 该表达式会作为观察数据在 <code>data</code> 选项中的键路径.
                                对于值 :
                            </p>
                            <ul>
                                <li>
                                    值为字符串时作为回调函数名,
                                </li>
                                <li>
                                    值为函数时作为回调函数, 回调函数有两个可选参数, 第一个参数为观察数据变动的新值, 第二个参数为旧值;
                                </li>
                                <li>
                                    值为对象时, 作为一个选项对象.
<br>
                                    选项<code>handler</code>可以是现在描述的任意4个值之一(不会真有人给<code>handler</code>选项继续套娃一个选项对象吧, 不会吧, 不会吧)
                                    必选项, 下面两项为可选
                                    <br>
                                    <code>immediate</code>选项值为boolean, 为<code>true</code>时回调将会在侦听开始时立刻调用, 未指定时默认为false.
                                    立刻调用时, 回调函数的参数中, 第一个参数为当前观察的数据的值, 第二个参数为<code>undefined</code>
                                    <br>
                                    <code>deep</code>为boolean值, 为true时指出回调会在侦被侦听对象的property 改变时调用,
                                    不论是改变该数据本身还是该数据内部嵌套的数据. 该选项在未指定时默认为false
                                </li>
                                <li>
                                    当值为数组时, 作为回调数组, 其中数组项为任意上面3种类型, 它们会被逐一调用
                                </li>
                            </ul>
                            <p>
                                示例 :
                            </p>

                            <pre>
let vm = new Vue({
    data: {
        a: 1, 
        b: 2,
        c: 3,
        d: {
            foo: 1,
            bar: 2,
            baz: {
                far: 3,
                faz: 4
            }
        },
        e: 5,
        f: {
            g: {
                h: 6
            }
        }
    },

    methods: {
        handler0(newV, oldV) {
            console.log(
                "string handler: \n" + 
                `newVaule=${ newV } oldValue=${ oldV }`
            );
        },

        handler1(newV, oldV) {
            console.log(
                "object handler, \n" +
                "string handler option, \n" + 
                "with immediate set to true: \n" +  
                `newVaule=${ newV } oldValue=${ oldV }`
            );
        },

        handler2(newV, oldV) {
            console.log(
                "array handler, \n" + 
                "string item: \n" + 
                `newVaule=${ newV } oldValue=${ oldV }`
            );
        },
    },

    watch: {
        // 值为字符串
        a: 'handler0',

        // 值为方法
        b: function (newV, oldV) {
            console.log(
                "method handler: \n" + 
                `newVaule=${ newV } oldValue=${ oldV }`
            );
        },

        // 值为对象, 
        c: {
            // handler选项为字符串
            handler: 'handler1',
            immediate: true
        },

        d: {
            // handler选项为函数
            handler: function (newV, oldV) {
                console.log(
                    "object handler, \n" +
                    "method handler option \n" +
                    "with deep set to true: \n" +
                    `newVaule=${ newV } oldValue=${ oldV }`
                );
            },
                
            deep: true
        },

        // 值为数组
        e: [
            'handler2',

            function hdler (newV, oldV) {
                console.log(
                    "array handler, \n" +
                    "method item : \n" + 
                    `newVaule=${ newV } oldValue=${ oldV }`
                );
            },

            {
                handler: function (newV, oldV) {
                    console.log(
                        "array handler, \n" + 
                        "object item : \n" + 
                        `newVaule=${ newV } oldValue=${ oldV }`
                    );
                }
            }
        ],

        // 为了观察嵌套的数据, 需要指定键路径 : 
        'f.g.h' : function (newV, oldV) {
            console.log(
                "key path handler: \n" + 
                `newValue=${newV} oldValue=${oldV}`
            );
        }
    }
});

vm.d.foo = 114514; // 将触发回调, 因为设置了deep 选项为true, 若设置为false则不会调用回调

vm.f.g = {}; // 如果通过键路径观察的数据被覆盖掉, 回调会调用一次表示观察数据的更改, 随后该观察回调函数失效
vm.f.g.h = 10; // 失效之后无论如何更改都无法触发回调, 因为此时加入的数据不是响应式的 
// (响应式数据必须在创建Vue实例时被定义在 data option中)

// 同理其他观察数据, 只要不是响应式的, 那么回调将不会被调用 : 
delete vm.a;
vm.a = 1; // 不触发回调
                            </pre>

                            <p class="text-warning bg-warning">
                                注意: 在观察变异(非替换)对象或数组, 旧值与新值相同, 因为它们的引用指向同一个对象/数组. Vue不会保留变异之前值的副本. 例如:
                            </p>

                            <pre>
let app = new Vue({
    data: {
        obj: {
            foo: 'bar'
        }
    },
    watch: {
        obj: {
            handler: function (newV, oldV) {
                console.log(newV === oldV);
            },
            deep: true
        }
    }
});

app.obj.foo = 'baz'; // => true
app.obj = {}; // => false
                            </pre>

                            <p>
                                注意 : 回调函数的触发不在于观察数据的setter是否被调用而在于观察数据是否改变, 也就是说调用setter给数据赋一个与旧值一样的值将不会触发回调
                            </p>

                            <p class="text-warning bg-warning">
                                不要用箭头函数定义watch函数
                            </p>

                            <br>
                            <br>

                            <p>
                                <code>$watch</code> 属于实例方法/数据. 它是一个方法, 定义为
                                <code>vm.$watch(expOrFn, callback, [options])</code>.
                            </p>
                            <ul>
                                <li>
                                    参数
                                    <ul>
                                        <li>
                                            expOrFn, 类型为 {string | Function}, 为string类型时作为要观察的数据的键路径,
                                            为函数类型时, 函数的<code>this</code>指向<code>vm</code>实例, 可以做一些有关<code>vm</code>的运算,
                                            并返回结果, 每当返回结果不同时回调函数就会被调用.
                                        </li>
                                        <li>
                                            callback, 类型为 {Function | Object}, 为函数时就作为回调函数, 为对象时作为选项对象,
                                            跟<code>watch</code>的选项对象一样
                                        </li>
                                        <li>
                                            options, 选项对象, 当第二个参数为函数时可以通过这个对象提供
                                            <code>immediate</code>和<code>deep</code>选项.
                                        </li>
                                    </ul>
                                </li>

                                <li>
                                    返回值 : <code>unwatch</code>, 类型为<code>Function</code>, 可以用来赋值给一个变量,
                                    以便需要时通过该变量调用函数停止对数据的观察.
                                </li>
                            </ul>

                            <p>
                                注意带有<code>immediate</code>选项时不能在第一次回调时取消侦听给定的property :
                            </p>
                            <pre>
// 报错
let unwatch = vm.$watch(
    'value',
    function () {
        unwatch()
    },
    { immediate: true }
)
                            </pre>

                            <p>
                                如果仍然希望在回调内部调用一个取消观察的函数, 应该先检查其函数的可用性:
                            </p>

                            <pre>
let unwatch = vm.$watch(
    'value',
    function () {
        if (unwatch) {
            unwatch();
        }
    },
    { immediate: true }
)
                            </pre>
                        </div>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="14">Class与Style绑定</h1>

                <article>
                    <p>
                        <code>v-bind</code>指令用于动态地绑定一个或多个attribute, 或者一个组件prop 到表达式上.
                    </p>

                    <p>
                        当指令有参数时, 参数作为绑定的attribute 或 prop, 指令表达式可以为任意值.
                    </p>

                    <p>
                        当该指令无参数时, 表达式应该期望一个对象作为值,
                        该对象的每一个属性名将作为指令参数, 属性值将作为指令的表达式使用.
                    </p>

                    <p>
                        指令使用修饰符 <code>.prop</code>时, 表达式将作为一个DOM property绑定而不是作为attribute绑定. ( <a
                           href="https://stackoverflow.com/questions/6003819/properties-and-attributes-in-html#answer-6004028">差别</a>
                        )
                    </p>

                    <p>
                        使用<code>.camel</code>修饰符, 可将kebab-case attribute名转换为camelCase
                    </p>

                    <p>
                        操作元素的class和内联样式是数据绑定的常见需求, 因为它们都是属性, 所以用<code>v-bind</code>处理: 只需要通过表达式计算出字符串结果即可.
                        不过字符串拼接麻烦且容易出错, 所以Vue做了专门的加强. 表达式结果类型除了是字符串, 还可以是数组或对象.
                    </p>
                </article>

                <div>

                </div>

                <article>
                    <h2 id="141">绑定HTML Class</h2>

                    <div>
                        <h4>对象语法</h4>
                        <div>
                            <p>
                                可以传给<code>v-bind:class</code>一个对象, 以动态切换class. 对象的键为类名, 值可以为任意表达式, 类是否存在取决于表达式的
                                <a data-toggle="tooltip"
                                   title="在 JavaScript 中，truthy（真值）指的是在布尔值上下文中，转换后的值为真的值。所有值都是真值，除非它们被定义为 假值（即除 false、0、''、null、undefined 和 NaN 以外皆为真值">真值(truthiness)</a>.
                                例如 :
                            </p>
                            <pre>&lt;div v-bind:class="{active: isActive}"&gt;&lt;/div&gt;</pre>
                            <p>
                                <code>active</code>类的存在与否取决于<code>isActive</code>的真值.
                            </p>

                            <br>

                            <p>
                                可以在对象中传入更多属性来动态切换多个class, 此外, <code>v-bind:class</code>指令可以与普通class属性共存, 例如 :
                            </p>
                            <pre>&lt;div class="static" v-bind:class="{active: isActive, 'text-danger': hasError}"&gt;&lt;/div&gt;</pre>
                            <pre>data: {isActive: true, hasError: false}</pre>
                            <p>
                                结果渲染为 :
                            </p>
                            <pre>&lt;div class="static active"&gt;&lt;/div&gt;</pre>

                            <br>

                            <p>绑定的数据对象不必内联定义在模板内, 总之指令<code>v-bind:class</code>可以接受任何真值为对象的表达式, 对象中所有值为真的属性名都会被加入到class中
                                :
                            </p>
                            <pre>&lt;div v-bind:class="classObject"&gt;&lt;/div&gt;</pre>
                            <pre>data: {classObject: {active: true, 'text-danger': false}}</pre>
                        </div>
                    </div>

                    <div>
                        <h4>数组语法</h4>
                        <div>
                            <p>
                                <code>v-bind</code>在绑定<code>class</code>的时候还可以接受一个值为数组的表达式.
                                每个数组项可以为一个表达式, 如果该表达式值为一个对象, 则按照对象语法的规则; 如果该表达式值为字符串则该字符串作为类名直接加入(空字符串不会加入)
                            </p>
                            <pre>&lt;div v-bind:class="[activeClass, errorClass]"&gt;&lt;/div&gt;</pre>
                            <pre>data: {activeClass: 'active', errorClass: 'text-danger'}</pre>

                            <br>

                            <p>
                                如果想根据条件切换列表中的class, 可以用三元表达式:
                            </p>
                            <pre>&lt;div v-bind:class="[isActive ? activeClass: '', errorClass]"&gt;&lt;/div&gt;</pre>
                            <p>
                                <code>activeClass</code>是否添加取决于<code>isActive</code>的真值
                            </p>
                            <p>
                                当有多个条件class时这样写会有些繁琐, 这时可以通过在数组项使用对象语法解决.
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>用于组件上</h4>
                        <div>
                            <p>
                                在一个自定义组件上用<code>class</code>属性时, 这些class会被添加到该组件的根元素上, 该元素已存在的class不会被覆盖
                            </p>
                            <p>
                                例如声明了组件 :
                            </p>
                            <pre>
Vue.component(
    'my-component', 
    {
        template: '&lt;p class="foo bar">Hi&lt;/p&gt;'
    }
)
                            </pre>
                            <p>
                                然后使用时添加一些class :
                            </p>
                            <pre>&lt;my-component class="baz boo"&gt;&lt;/my-component&gt;</pre>
                            <p>
                                HTML将渲染为
                            </p>
                            <pre>&lt;p class="foo bar baz boo"&gt;Hi&lt;/p&gt;</pre>
                        </div>
                    </div>
                </article>


                <article>
                    <h2 id="142">绑定内联样式</h2>

                    <div>
                        <h4>对象语法</h4>
                        <div>
                            <p>
                                <code>v-bind:style</code>的对象语法看起来很像css但其实是一个js对象, 指令表达式可以为任意值为对象的表达式, 其属性名会作为css的属性名,
                                属性值作为css属性值. css属性名可用驼峰式(camelCase)
                                或短横线分隔(记得加引号, 因为 <code>-</code> 在js中不能作为标识符的一部分) 来命名.
                            </p>

                            <p>
                                例如我们可以使用字面量对象来绑定样式.
                            </p>
                            <pre>&lt;div v-bind:style="{color: activeColor, fontSize: fontSize: + 'px'}"&gt;&lt;/div&gt;</pre>

                            <p>
                                通常直接绑定到一个样式对象中可以使模板更加清晰 :
                            </p>
                            <pre>&lt;div v-bind:style="styleObject"&gt;&lt;/div&gt;</pre>
                            <pre>data: {styleObject: {color: "red", fontSize: "10px"}}</pre>

                        </div>
                    </div>

                    <div>
                        <h4>数组语法</h4>
                        <div>
                            <p>
                                指令表达式还可以绑定到一个值为数组的表达式,
                                项为样式对象, 例如 :
                            </p>

                            <pre>&lt;div v-bind:style="[positioningStyle, decorationStyle]"&gt;&lt;/div&gt;</pre>

                            <pre>
data: {
    positioningStyle: {
        position: "relative", 
        left: "1px"
    }, 
    decorationStyle: {
        color: "red", 
        backgroudColor: "blue"
    }
}
                            </pre>
                        </div>
                    </div>

                    <div>
                        <h4>自动添加前缀</h4>
                        <div>
                            <p>
                                当<code>v-bind:style</code>使用需要添加浏览器引擎前缀的css属性时, Vue.js自动侦测并添加响应前缀
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>多重值</h4>
                        <div>
                            <p>
                                从2.3.0开始可以为<code>style</code>绑定中的属性提供一个包含多个值的数组, 常用于提供多个带前缀的值, 例如 :
                            </p>
                            <pre>&lt;div :style="{display: ['-webkit-box', '-ms-flexbox', 'flex']}"&gt;&lt;/div&gt;</pre>
                            <p>
                                这样写会渲染数组中最后一个被浏览器支持的值
                            </p>
                        </div>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="15">条件渲染</h1>

                <article>
                    <h2 id="151">v-if</h2>

                    <div>
                        <p>
                            <code>v-if</code>指令用于条件性渲染一块内容(根据表达式的真值), 配合使用<code>v-else-if</code>和<code>v-else</code>, 例如
                            :
                        </p>
                        <pre>&lt;div id="app-10"&gt; <br>  &lt;div v-if="type === 'A'"&gt;A&lt;/div&gt;  <br>  &lt;div v-else-if="type === 'B'"&gt;B&lt;/div&gt; <br>  &lt;div v-else&gt;C&lt;/div&gt; <br>&lt;/div&gt;</pre>
                        <pre>
let app10 = new Vue({
    el: "#app-10",
    data: {
        type: 'A'
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-10">
                                    <div v-if="type === 'A'">A</div>
                                    <div v-else-if="type === 'B'">B</div>
                                    <div v-else>C</div>
                                </div>
                            </samp>
                        </div>

                        <p>尝试在控制台修改<code>app10.type</code>的值可以观察到内容的响应式更新</p>
                    </div>

                    <div>
                        <h4>在<code>&lt;template&gt;</code>上使用<code>v-if</code></h4>
                        <div>
                            <p>
                                因为<code>v-if</code>是一个指令, 所以必须添加到一个元素上. 为了切换多个元素, 可以把一个<code>&lt;template&gt;</code>
                                元素当作不可见的包裹元素, 并在上面使用<code>v-if</code>, 如果条件为真, 最终渲染条件将不包括它, 而其子元素被全部渲染.
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>用<code>key</code>管理可复用元素</h4>
                        <div>
                            <p>
                                Vue会尽可能高效渲染元素, 通常会复用已有元素而不是从头渲染. 这样除了让Vue更加快之外还有其他好处. 例如如果允许用户用不同方式登录(用户名或邮箱) ：
                            </p>

                            <pre>
&lt;div id="app-11"&gt;
    &lt;template v-if="loginType === 'username'"&gt;
        &lt;label&gt;Username&lt;/label&gt;
        &lt;input type="text"
                  placeholder="enter your username"&gt;
    &lt;/template&gt;

    &lt;template&gt;
        &lt;label&gt;Email&lt;/label&gt;
        &lt;input type="text"
                  placeholder="enter your email"&gt;
    &lt;/template&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let app11 = new Vue({
    el: "#app-11",
    data: {
        loginType: "username"
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-11">
                                        <template v-if="loginType === 'username'">
                                            <label>Username</label>
                                            <input type="text"
                                                   placeholder="enter your username">
                                        </template>
                                        <template v-else>
                                            <label>Email</label>
                                            <input type="text"
                                                   placeholder="enter your email">
                                        </template>
                                    </div>
                                </samp>
                            </div>

                            <p>
                                尝试在控制台修改<code>app11.loginType</code>, 可以看见切换后已输入内容不会被重新渲染
                            </p>

                            <br>

                            <p>
                                但这并不总是符合实际. 因此Vue提供了一种方式来表达"这两个元素完全独立, 不要复用它们". 只需要为渲染元素添加一个唯一的<code>key</code>值即可 :
                            </p>

                            <pre>
&lt;div id="app-12"&gt;
    &lt;template v-if="loginType === 'username'"&gt;
        &lt;label&gt;Username&lt;/label&gt;
        &lt;input type="text"
                  placeholder="enter your username"
                  key="username-input"&gt;
    &lt;/template&gt;

    &lt;template&gt;
        &lt;label&gt;Email&lt;/label&gt;
        &lt;input type="text"
                  placeholder="enter your email"
                  key="email-input"&gt;
    &lt;/template&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let app12 = new Vue({
    el: "#app-12",
    data: {
        loginType: "username"
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-12">
                                        <template v-if="loginType === 'username'">
                                            <label>Username</label>
                                            <input type="text"
                                                   placeholder="enter your username"
                                                   key="username-input">
                                        </template>
                                        <template v-else>
                                            <label>Email</label>
                                            <input type="text"
                                                   placeholder="enter your email"
                                                   key="email-input">
                                        </template>
                                    </div>
                                </samp>
                            </div>

                            <p>
                                这次, 在控制台切换 <code>app12.loginType</code>值时, input里的内容会被重新渲染.
                                注意label仍然会被高效复用, 如果它没有添加<code>key</code>属性
                            </p>

                            <br>

                            <p>
                                <code>key</code> attribute在Vue api中属于特殊attribute, 预期值为 <code>number | string</code>,
                            </p>
                            <p>
                                <code>key</code>主要用在Vue的虚拟DOM算法中, 新旧nodes对比时辨识VNodes. 不使用时,
                                Vue会使用一种最大限度减少动态元素并尽可能地尝试就地修改/复用相同类型元素的算法. 使用key时, 它会基于key的变化重新排列元素顺序, 并且移除key不存在的元素.
                            </p>
                            <p>
                                有相同父元素的子元素必须有<strong>唯一(unique)的key</strong>. 重复的key会造成渲染错误.
                            </p>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="152">v-show</h2>

                    <div>
                        <p>
                            <code>v-show</code>也是根据条件渲染的指令, 不同的是, 它不支持template元素,
                            也不支持<code>v-else-if</code>和<code>v-else</code>
                            , 并且带有它的元素始终会被渲染并保留在DOM中, <code>v-show</code>做的只是简单切换元素的css属性<code>display</code>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="153">v-if vs v-show</h2>

                    <div>
                        <p>
                            <code>v-if</code>为"真正"的条件渲染, 因为它确保切换过程中条件块内的事件监听器和子组件适当地被销毁和重建.
                        </p>
                        <p>
                            <code>v-if</code>也是惰性的: 如果初始渲染条件为假, 则什么也不做——直到条件第一次变为真, 才会开始渲染条件块
                        </p>
                        <p>
                            <code>v-show</code>无论初始条件如何, 元素总会被渲染, 且只简单地基于css切换
                        </p>
                        <p>
                            一般<code>v-if</code>有更高地切换开销, 而<code>v-show</code>有更高地初始渲染开销.
                            因此, 若需要频繁切换, 则使用<code>v-show</code>, 若运行时条件变化很少, 则用<code>v-if</code>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="154">v-if 和 v-for</h2>

                    <div>
                        <div class="alert-warning">不推荐同时使用</div>
                        <p>
                            当同时使用时, <code>v-for</code>优先级更高
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="16">列表渲染</h1>

                <article>
                    <p>
                    </p>

                    <p>
                        <code>v-for</code>指令 表达式语法为 <code>alias in express</code>, 其中，
                        <code>express</code>预期值为 : <code>Array | Object | number | string | Iterable (2.6新增)</code>.
                    </p>
                </article>

                <article>
                    <h2 id="161">把数组对应为一组元素</h2>
                    <div>
                        <p>
                            可以用<code>v-for</code>指令来基于数组渲染列表li(也可以是任意元素). <code>v-for</code> attribute的值需要用形如
                            <code>item in items</code>的特殊语法, 其中<code>items</code>为定义在<code>data</code>对象中的一个值为数组的属性,
                            又或者来自计算属性, 或者来自函数返回值 ...
                            作为源数据数组, 而<code>item</code>则是被迭代的数组元素的<strong>别名</strong>(alias). 例如 :
                        </p>
                        <pre>&lt;ul id="app-13"&gt;<br>  &lt;li v-for="item in items"&gt;{{item.message}}&lt;/li&gt; <br>&lt;/ul&gt;</pre>
                        <pre>
let app13 = new Vue({
    el: "#app-13",
    data: {
        items: [
            { message: "Apple" },
            { message: "Banana" },
            { message: "Orrange" }
        ]
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="app-13">
                                    <li v-for="item in items">{{item.message}}</li>
                                </ul>
                            </samp>
                        </div>

                        <br>

                        <p>
                            在<code>v-for</code>块中(使用该指令的元素的content), 我们可以通过双括号语法访问该item及其父域(此处 <code>items</code> 定义在
                            <code>data</code>中, 父域为 <code>data</code>, 如果 <code>items</code> 为一个返回数组的计算属性,
                            那么块内就可以访问所有计算属性)
                            的所有属性. 同时<code>v-for</code>的值可以接受第二个参数, 和项参数一起用逗号分隔并用圆括号括起来, 该参数为当前项的索引, 例如 :
                        </p>
                        <pre>v-for="(item, index) in items"</pre>

                        <br>

                        <p>
                            js中, 有for-of循环用于迭代可迭代的对象, 因此可以用<code>of</code>替代<code>in</code>, 告诉代码阅读者, 数据源为可迭代对象,
                            但两者效果是一样的.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="162">v-for内使用对象</h2>
                    <div>
                        <p>
                            js也有for-in循环用于枚举一个可枚举(enumerable)对象的所有属性, 因此可以用<code>v-for</code>来遍历一个对象的属性, 例如 :
                        </p>

                        <pre>&lt;ul id="app-14"&gt;<br>  &lt;li v-for="value in object"&gt;<br>    {{value}} <br>  &lt;/li&gt; <br>&lt;/ul&gt;</pre>

                        <pre>
let app14 = new Vue({
    el: "#app-14",
    data: {
        object: { foo: 1, bar: 2 }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="app-14">
                                    <li v-for="value in object">
                                        {{value}}
                                    </li>
                                </ul>
                            </samp>
                        </div>

                        <br>
                        <p>
                            使用对象时, <code>v-for</code>的值还可以使用第二个参数(property名称, 即键名)和第三个参数(即索引), 例如:
                        </p>
                        <pre>v-for="(value, name, index) in object"</pre>
                    </div>
                </article>

                <article>
                    <h2 id="163">维护状态</h2>
                    <div>
                        <p>
                            当Vue正在更新使用<code>v-for</code>渲染的元素列表时, 它默认使用"就地更新"的策略. 如果数据项的顺序被改变, Vue
                            将不会移动DOM元素来匹配数据项的顺序被改变, 而是就地更新每个元素, 并确保它们在每个索引位置的正确渲染.
                        </p>
                        <p>
                            这种默认模式是高效的, 但是只适用于不依赖子组件状态或临时DOM状态 (例如表单输入值)的列表渲染输出
                        </p>
                        <p>
                            为了给Vue一个提示, 以便它能够跟踪每个节点的身份, 从而重用和重新排序现有元素, 需要为每项提供一个唯一的<code>key</code>属性 :
                        </p>
                        <p>
                            应该尽可能在使用<code>v-for</code>时提供<code>key</code> attribute, 除非遍历输出的DOM内容非常简单,
                            或者刻意去依赖默认行为以获得性能提升
                        </p>

                        <div class="bg-warning text-warning">
                            应该使用primitive type值作为key值, 不要使用对象或数组值类的非基本类型值.
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="164">数组更新检测</h2>

                    <div>
                        <h4>变异方法(mutation method)</h4>
                        <div>
                            <p>
                                Vue将被侦听的数组的变异方法进行了包裹, 使用它们将触发视图view的更新, 方法包括 :
                                <ul>
                                    <li>push</li>
                                    <li>pop</li>
                                    <li>shift</li>
                                    <li>unshift</li>
                                    <li>splice</li>
                                    <li>sort</li>
                                    <li>reverse</li>
                                </ul>
                            </p>
                            <p>例如 : </p>
                            <pre>
&lt;ul id="app-15&gt;
    &lt;li v-for="item in items"&gt;{{item}}&lt;/li&gt;
&lt;/ul&gt;
                            </pre>

                            <pre>
let app15 = new Vue({
    el: "#app-15",
    data: {
        items: [1, 1, 4, 5, 1, 4]
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <ul id="app-15">
                                        <li v-for="item in items">{{item}}</li>
                                    </ul>
                                </samp>
                            </div>

                            <p>
                                可以在控制台中对<code>app15.items</code>调用上述方法观察列表变化
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>替换数组</h4>
                        <div>
                            <p>
                                数组的非变异方法(non-mutating method)不会改变源数组, 而是返回新数组. 当使用非变异方法时, 可以用新数组替换旧数组以触发视图更新, 例如 :
                            </p>
                            <pre>vm.items = vm.items.filter(function (item) {return item % 2 === 0;})</pre>

                            <br>
                            <p>
                                Vue为了使得DOM元素得到最大返回的重用而实现了一些只能的启发式方法, 而非仅仅丢弃现有DOM并重新渲染整个列表. 因此用一个含有相同
                                元素的数组去替换原来的数组时非常高效的.
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>注意事项</h4>
                        <div>
                            <p>
                                因js限制, Vue不能检测一下数组的变动 :
                                <ol>
                                    <li>直接通过索引修改数组项</li>
                                    <li>直接通过length属性修改数组长度</li>
                                </ol>
                                <br>
                            </p>
                            <p>
                                对于第一类问题, 通过<code>Vue.set(array, indexOfItem, newValue)</code>、
                                <code>Array.prototype.splice(indexOfItem, 1, newValue)</code>
                                或者 <code>vm.$set(array, indexOfItem, newValue)</code>解决.
                                原生js的splice方法以第一个参数为开始剪切的下标, 第二个参数为剪切长度, 第三个参数为剪切部分的替代值. 因此调用
                                <code>splice(indexOfItem, 1, newValue)</code>能够正确修改项值
                            </p>

                            <br>

                            <p>
                                对于第二类问题, 还是可以通过调用<code>splice(newLength)</code>解决. 给出了第一个参数, 第二和第三个参数省略, 将
                                使得数组从第一个参数开始的位置一直剪切到最后, 由于第三个参数没有给出, 因此剪切部分不会被新值替代, 从而得到期望的数组长度(若给出
                                的长度值大于原数组长度则不会发生任何变化).
                            </p>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="165">对象变更检测注意事项</h2>
                    <div>
                        <p>
                            仍然时由于js限制, Vue不能检测对象属性的增加或删除.
                            例如 :
                        </p>

                        <pre>&lt;ul id="app-16"&gt;<br>  &lt;li v-for="value in object"&gt;{{value}}&lt;/li&gt;<br>&lt;/ul&gt;</pre>

                        <pre>
let app16 = new Vue({
    el: "#app-16",
    data: {
        object: {
            a: 1,
            b: 2,
            c: 3
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <ul id="app-16">
                                <li v-for="value in object">{{value}}</li>
                            </ul>
                        </div>

                        <p>
                            那么, 调用删除属性如<code>delete app16.object.a</code>或者添加属性如<code>app16.object.d = 4</code>都是非响应式的,
                            不会更新到视图上.
                        </p>

                        <p>
                            如果想要响应式地删除一个对象的属性, 要使用全局api <code>Vue.delete</code>或者实例方法/数据中的<code>vm.$delete</code>,
                            这两个是等价的.
                        </p>
                        <p>
                            这两个方法的第一个参数为 <code>target</code>, 类型为<code>{Object | Array}</code>, 表示要进行响应式删除的对象/数组.
                        </p>
                        <p>
                            第二个参数为 一个<code>string</code>作为想要删除的属性名, 或一个数字<code>number</code>作为想要删除的项的下标.
                        </p>
                        <p>
                            该方法没有返回值.
                        </p>

                        <br>

                        <p>
                            对于已经创建的实例, Vue不允许动态添加根级别的响应式属性. 但是可以使用<code>Vue.set(object, propName, value)</code>
                            方法向嵌套对象添加响应式属性, 或者使用Vue实例的<code>$set(object, propName, value)</code>方法.
                        </p>

                        <p>
                            全局api <code>Vue.set</code>和实例方法/数据中的<code>vm.$set</code>(两者等价).
                        </p>

                        <p>
                            第一个参数为 <code>target</code>, 类型为 <code>{Object | Array}</code>, 表示想要进行响应式属性添加、修改属性的对象 或
                            想要进行响应式设置数组项的数组(注意如果设置项大于数组长度会导致除了设置项以外的新增项为 <code>undefined</code>, 渲染出空列表项 )
                        </p>

                        <p>
                            第二个参数为响应式设置的属性名( <code>string</code> 类型) 或 响应式设置的数组项( <code>number</code> 类型).
                        </p>

                        <p>
                            第三个参数为设置的值(任意类型)
                        </p>

                        <p>
                            函数返回设置的值
                        </p>

                        <br>

                        <p>
                            至此, 实例方法/数据中的三个方法已经全部了解了 : <code>$watch</code>, <code>$set</code>, <code>$delete</code>
                        </p>

                        <br>

                        <p>
                            为了为已有对象赋值多个属性, 可以使用<code>Object.assign(targetObject, ...sourceObjects)</code>, 该方法
                            为将源对象中的属性全部赋值给目标对象, 并返回该目标对象
                        </p>

                        <p>
                            但是, 为了使添加是响应式的, 不应该直接调用, 例如 : <code>Object.assign(vm.object, object1, object2)</code>,
                            而是应该调用 <code>vm.object = Object.assign({}, vm.object, object1, object2)</code>,
                            这种方式首先先创建了一个空对象
                            作为目标对象, 然后再把<code>vm.object</code>以及剩下的对象作为源对象将它们的属性赋值给该空对象, 最后再将这个新的对象赋值给
                            <code>vm.object</code>.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="166">显示过滤/排序后结果</h2>
                    <div>
                        <p>
                            有时为了显示一个数组经过排序或过滤后的版本而不值即改变或重写原数据, 可以创建一个计算属性, 用于返回经过处理后的数据, 例如 :
                        </p>
                        <pre>&lt;ul id="app-17"&gt;<br>  &lt;li v-for="n of evenNumbers"&gt;{{n}}&lt;/li&gt;<br>&lt;/ul&gt;</pre>

                        <pre>
let app17 = new Vue({
    el: "#app-17",
    data: {
        numbers: [0, 1, 2, 3, 4, 5]
    },
    computed: {
        evenNumbers: function () {
            return this.numbers.filter(n => n % 2 === 0);
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="app-17">
                                    <li v-for="n of evenNumbers">{{n}}</li>
                                </ul>
                            </samp>
                        </div>

                        <br>

                        <p>
                            在计算属性不适应的情况下 (例如, 在嵌套 <code>v-for</code>循环中), 可以使用一个方法 :
                        </p>

                        <pre>
&lt;div id="nest-for-demo"&gt;
    &lt;ul v-for="set of sets"&gt;
        &lt;li v-for="n of even(set)"&gt;
            {{ n }}
        &lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let nestForDemo = new Vue({
    el: "#nest-for-demo",
    data: {
        sets: [
            [0, 1, 2, 3],
            [10, 11, 12, 13],
            [20, 21, 22 ,23]
        ]
    },
    methods: {
        even(array) {
            return array.filter(n => n % 2 === 0)
        }
    }
});  
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="nest-for-demo">
                                    <ul v-for="set of sets">
                                        <li v-for="n of even(set)">
                                            {{ n }}
                                        </li>
                                    </ul>
                                </div>
                            </samp>
                        </div>

                    </div>
                </article>

                <article>
                    <h2 id="167">在v-for里使用值范围</h2>
                    <div>
                        <p>
                            <code>v-for</code>的值也可以接受一个整数, 这种情况下它会将模板重复对于次数, 项值为当前次数, 例如: <code>v-for="n of 10"</code>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="168">在template元素上使用v-for</h2>
                    <div>
                        <p>
                            类似于<code>v-if</code>, 可用template元素来循环渲染一段包含多个兄弟级元素的内容 :
                        </p>

                        <pre>
&lt;div id="template-for-demo&gt;
    &lt;template v-for="item of items"&gt;
        &lt;li&gt;{{ item.msg }}&lt;/li&gt;
        &lt;li class="divider"&gt;&lt;/li&gt;
    &lt;/template&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let templateForDemo = new Vue({
    el: "#template-for-demo",
    data: {
        items: [
            {msg: 'foo'},
            {msg: 'bar'},
            {msg: 'baz'}
        ]
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="template-for-demo">
                                    <template v-for="item of items">
                                        <li>{{ item.msg }}</li>
                                        <li class="divider"></li>
                                    </template>
                                </ul>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="169">v-for 和 v-if一起使用</h2>
                    <div>
                        <p>
                            不推荐一起使用. 当两条指令被加到同一元素上, <code>v-for</code>优先级高于<code>v-if</code>, 意味着<code>v-if</code>
                            将分别重复运行于每个<code>v-for</code>循环中. 当只想为部分项渲染节点, 这种机制将非常有用, 例如 :
                        </p>
                        <pre>&lt;ul id="app-18"&gt;<br>  &lt;li v-for="todo of todos" v-if="!todo.isComplete"&gt;{{todo.taskname}}&lt;/li&gt;<br>&lt;/ul&gt;</pre>

                        <pre>
let app18 = new Vue({
    el: "#app-18",
    data: {
        todos: [
            { taskname: "a", isComplete: false },
            { taskname: "b", isComplete: true },
            { taskname: "c", isComplete: false }
        ]
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="app-18">
                                    <li v-for="todo of todos"
                                        v-if="!todo.isComplete">{{todo.taskname}}</li>
                                </ul>
                            </samp>
                        </div>
                        <p>以上只渲染未完成的todo项</p>
                    </div>
                </article>

                <article>
                    <h2 id="1610">在组件上使用v-for</h2>
                    <div>
                        <p>
                            在自定义组件上可以像任何普通元素一样使用<code>v-for</code>, 例如 :
                        </p>
                        <pre>&lt;my-component v-for="item in items :key="item.id"&gt;&lt;/my-component&gt;</pre>
                        <p>
                            <div class="alert-info">
                                在2.2.0+版本里, 自定义组件上使用<code>v-for</code>时, <code>key</code> attribute是必须的
                            </div>
                        </p>

                        <br>

                        <p>
                            然而, 任何数据不会被自动传递到组件里, 因为组件有自己独立的作用域. 为了把迭代数据传到组件中, 应该使用prop, 例如 :
                        </p>
                        <pre>&lt;my-component <br>  v-for="(item, index) in items"<br>  v-bind:item="item"<br>  v-bind:index="index"<br>  v-bind:key="item.id"<br>&gt;&lt;/my-component&gt;</pre>

                        <br>

                        <p>
                            不自动将<code>item</code>注入到组件的原因是, 这会使得组件与<code>v-for</code>的运作紧密耦合. 明确组件数据的来源能够使组件在其他场合重复使用
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="17">事件处理</h1>

                <article>
                    <h2 id="171">监听事件</h2>
                    <div>
                        <p>
                            <code>v-on</code>指令可用于监听DOM事件, 并在触发时执行一些js代码, 例如 :
                        </p>
                        <pre>&lt;div id="app-19"&gt;<br>  &lt;button @click="counter += 1"&gt;Add&lt;/button&gt;<br>  &lt;p&gt;Clicked {{counter}} times.&lt;/p&gt;<br>&lt;/div&gt;</pre>

                        <pre>
let app19 = new Vue({
    el: "#app-19",
    data: {
        counter: 0
    }
}); 
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-19">
                                    <button @click="counter += 1">Add</button>
                                    <p>Clicked {{counter}} times.</p>
                                </div>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="172">事件处理方法</h2>
                    <div>
                        <p>
                            许多事情逻辑复杂, 所以直接把js代码写在<code>v-on</code>指令使不可行的. 因此<code>v-on</code>还可以
                            接受一个需要调用的方法名称, 随后在<code>methods</code>选项中定义该方法即可.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="173">内联处理器方法</h2>
                    <div>
                        <p>
                            除了直接绑定到一个方法, 还可以直接在内联js语句中调用方法
                        </p>
                        <p>
                            在需要访问原始的DOM事件时这种方法比较有用, 可以通过特殊变量<code>$event</code>将它传入方法
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="174">事件修饰符</h2>
                    <div>
                        <p>
                            在事件处理程序中调用<code>event.preventDefault()</code>或<code>event.stopPropagation()</code>非常常见.
                            尽管可以在方法中轻松实现, 但更好的方式是: 方法只有纯粹的数据逻辑, 而不是去处理DOM事件细节.
                        </p>

                        <br>

                        <p>
                            为了解决这个问题, Vue.js为<code>v-on</code>指令提供了<strong>事件修饰符</strong>. 修饰符是由点开头的指令后缀来表示的, 且可以串联.
                            <ul>
                                <li>.stop : 阻止click事件继续传播 ( 等价于调用<code>event.stopPropagation()</code> )</li>
                                <li>.prevent : 阻止默认行为</li>
                                <li>.capture : 添加事件监听器时使用事件捕获模式, 即子元素触发的事件先由该元素处理, 再交给子元素处理</li>
                                <li>.self : 只当event.target 是当前元素自己才触发</li>
                                <li>.once : 事件只触发一次(2.1.4新增)</li>
                                <li>.passive : 事件的默认行为立刻触发, 不会等待事件处理器完成 (尤其提升移动端性能)</li>
                            </ul>
                        </p>

                        <p class="alert-warning">
                            串联使用时, 顺序很重要; 修饰器背后的相应代码会以同样的顺序产生.
                        </p>

                        <p class="alert-warning">
                            不要把<code>.passive</code>和<code>.prevent</code>一起使用
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="175">按键修饰符</h2>
                    <div>
                        <p>
                            在监听按键事件, 经常需要检查详细按键. Vue允许为<code>v-on</code>在监听键盘事件时添加按键修饰符 :
                        </p>
                        <pre>&lt;input id="app-20" @keyup.enter="enter" placeholder="press enter to log message"&gt;</pre>
                        <pre>
let app20 = new Vue({
    el: "#app-20",
    methods: {
        enter: function () {
            console.log("enter key pressed");
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <input id="app-20"
                                       @keyup.enter="enter"
                                       placeholder="press enter to log message">
                            </samp>
                        </div>

                        <h4>按键码</h4>
                        <p>
                            Vue提供了绝大多数常用的按键码的别名 :
                        </p>

                        <ul>
                            <li>.enter</li>
                            <li>.tab</li>
                            <li>.delete</li>
                            <li>.esc</li>
                            <li>.space</li>
                            <li>.up</li>
                            <li>.down</li>
                            <li>.left</li>
                            <li>.right</li>
                        </ul>

                        <p>
                            我们还可以通过全局配置(<code>Vue.config</code>)中的<code>keyCodes</code>对象为键定义按键码.
                            该对象的属性名为需要设置按键码的键别名, 属性值为<code>number | Array&lt;number&gt;</code>
                    </div>
                </article>

                <article>
                    <h2 id="176">系统修饰符</h2>
                    <div>
                        <p>
                            可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器 :
                        </p>
                        <ul>
                            <li>.ctrl</li>
                            <li>.alt</li>
                            <li>.shift</li>
                            <li>.meta, 在window上对应win键</li>
                            <li>.exact : 用于控制精确的系统修饰符组合触发的事件</li>
                            <li>.left : 鼠标左键</li>
                            <li>.right : 鼠标右键</li>
                            <li>.middle : 鼠标中键</li>
                        </ul>

                        <br>
                        <p>例如 : </p>
                        <pre>
&lt;input id="app-21" 
          @keyup.ctrl.c="copy"
          placeholder="press ctrl c to log message"&gt;

                        </pre>

                        <pre>
let app21 = new Vue({
    el: "#app-21",
    methods: {
        copy: function () {
            console.log("ctrl + c pressed");
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <input id="app-21"
                                       @keyup.ctrl.c="copy"
                                       placeholder="press ctrl c to log message">
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="177">为什么在HTML中监听事件?</h2>
                    <div>
                        <p>
                            可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。
                            但不必担心，因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的
                            ViewModel 上，它不会导致任何维护上的困难。实际上，使用 <code>v-on</code> 有几个好处：
                        </p>
                        <ol>
                            <li>扫描一眼HTML模板便能够轻松定位js代码对应方法</li>
                            <li>因为无需在js手动绑定事件, ViewModel代码可以是非常纯粹的逻辑, 和DOM完全解耦, 便于测试</li>
                            <li>当一个ViewModel被销毁时, 所有事件处理器自动删除, 无需担心如何清理</li>
                        </ol>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="18">表单输入绑定</h1>

                <article>
                    <h2 id="181">基本用法</h2>
                    <div>
                        <p>
                            可以用<code>v-model</code>指令在表单控件元素input, textarea, select上创建双向数据绑定. 它会根据控件类型自动选取正确的方法来更新元素.
                            <code>v-model</code>本质上为语法糖, 例如 :
                        </p>

                        <pre>&lt;input v-model="demo"&gt;</pre>

                        <p>
                            等价于 : <br>
                        </p>

                        <pre>&lt;input v-bind:value="demo" v-on:input="demo = $event.target.value"&gt;</pre>

                        <p>
                            它负责监听用户的输入事件以更新数据, 并对一些极端场景进行一些特殊处理.
                        </p>

                        <p>
                            但两者有细微的区别: 使用 <code>v-model</code> 不会对输入法输入过程的消息进行绑定, 而使用 <code>v-bind</code>加
                            <code>v-on</code>会在输入法输入的过程中对绑定数据进行更新.
                        </p>

                        <p class="alert-warning">
                            <code>v-model</code>忽略所有表单控件的<code>value</code>, <code>checked</code>,
                            <code>selected</code>
                            attribute的
                            初始值而总是将Vue实例的数据作为数据来源. 应该通过js在组件的<code>data</code>选项中声明这些attribute的初始值.
                        </p>

                        <br>

                        <p>
                            <code>v-model</code> 在内部为不同的输入元素使用不同的属性并抛出不同的事件 :
                            <ul>
                                <li>input:text和textarea元素使用<code>value</code>属性和<code>input</code> 事件</li>
                                <br>
                                <li>
                                    input:checkbox和input:radio使用<code>checked</code>和<code>change</code>,
                                    绑定在相同数据上的checkbox或radio会被组成一组(类似于HTML需要让相同组的checkbox或radio拥有相同的name attribute). <br>
                                    <br>
                                    此外, input:radio根据其value attribute对<code>v-model</code>绑定数据进行更新(若不给出value
                                    attribute值会导致无法正常更新); <br> <br>

                                    单个input:checkbox可以通过v-model绑定到数组(利用value attribute 更新)或非数组值(利用checked attribute更新)上;
                                    而多个checkbox只能绑定到一个数组上同时必须提供它们的value attribute, 否则无法正常工作
                                </li>
                                <br>
                                <li>select使用<code>value</code>作为prop和<code>change</code>事件</li>
                            </ul>
                        </p>
                    </div>

                    <div>
                        <h4>input:text</h4>
                        <div>
                            <pre>
&lt;div id="app-22"&gt;
    &lt;input v-model="message" placeholder="edit me"&gt;
    Message : {{message}}
&lt;/div&gt;</pre>

                            <pre>
let app22 = new Vue({
    el: "#app-22",
    data: {
        message: ""
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-22">
                                        <input v-model="message"
                                               placeholder="edit me">
                                        Message : {{message}}
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>

                    <div>
                        <h4>textarea</h4>
                        <div>
                            <pre>&lt;div id="app-23"&gt;<br>  &lt;textarea v-model="message" placeholder="edit me"&gt;&lt;/textarea&gt;<br>  &lt;p&gt;Message : {{message}}&lt;/p&gt;<br>&lt;/div&gt;</pre>
                            <pre>
let app23 = new Vue({
    el: "#app-23",
    data: {
        message: ""
    }
});
                            </pre>

                            <div id="app-23">
                                <textarea v-model="message"
                                          placeholder="edit me"></textarea>
                                <p>Message : {{message}}</p>
                            </div>
                        </div>
                    </div>

                    <div>
                        <h4>复选框</h4>
                        <div>
                            <p>单个复选框, 绑定到布尔值</p>
                            <pre>&lt;div id="app-24"&gt;<br>  &lt;input type="checkbox" v-model="checked"&gt;<br>  &lt;label for="checkbox"&gt;{{checked}}&lt;/label&gt;<br>&lt;/div&gt;</pre>
                            <pre>
let app24 = new Vue({
    el: "#app-24",
    data: {
        checked: false
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-24">
                                        <input type="checkbox"
                                               id="checkbox"
                                               v-model="checked">
                                        <label for="checkbox">{{checked}}</label>
                                    </div>
                                </samp>
                            </div>

                            <br>

                            <p>对于多个复选框, 用<code>v-model</code>将各个复选项绑定到一个数组上</p>
                            <pre>
&lt;div id="app-25"&gt;
    &lt;input type="checkbox" 
              id="a" 
              value="a" 
              v-model="checked"&gt;
    &lt;label for="a"&gt;a&lt;/label&gt;
    
    &lt;input type="checkbox" 
              id="b" 
              value="b" 
              v-model="checked"&gt;
    &lt;label for="b"&gt;b&lt;/label&gt;
    
    &lt;input type="checkbox" 
              id="c" value="c" 
              v-model="checked"&gt;
    &lt;label for="c"&gt;c&lt;/label&gt;
    
    &lt;p&gt;Checked : {{checked}}&lt;/p&gt;<br>
&lt;/div&gt;</pre>
                            <pre>
let app25 = new Vue({
    el: "#app-25",
    data: {
        picked: []
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-25">
                                        <input type="checkbox"
                                               value="a"
                                               v-model="picked">
                                        <label for="a">a</label>

                                        <input type="checkbox"
                                               value="b"
                                               v-model="picked">
                                        <label for="b">b</label>

                                        <input type="checkbox"
                                               value="c"
                                               v-model="picked">
                                        <label for="c">c</label>

                                        <p>Checked : {{picked}}</p>
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>

                    <div>
                        <h4>单选按钮</h4>
                        <div>

                            <pre>
&lt;div id="app-26"&gt; 
    &lt;input type="radio" 
              value="one"
              v-model="picked"&gt;
    &lt;label for="one"&gt;one&lt;/label&gt;
    
    &lt;input type="radio"  
              value="two"
              v-model="picked"&gt;
    &lt;label for="two"&gt;two&lt;/label&gt;
    
    &lt;p&gt;picked : {{picked}}&lt;/p&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let app26 = new Vue({
    el: "#app-26",
    data: {
        picked: 'two'
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-26">
                                        <input type="radio"
                                               id="one"
                                               value="one"
                                               v-model="picked">
                                        <label for="one">one</label>
                                        <input type="radio"
                                               id="two"
                                               value="two"
                                               v-model="picked">
                                        <label for="two">two</label>
                                        <p>picked : {{picked}}</p>
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>

                    <div>
                        <h4>选择框</h4>
                        <div>
                            <p>单选</p>

                            <pre>
&lt;div id="app-27"&gt;
    &lt;select v-model="selected"&gt;
        &lt;option disabled
                   value=""&gt;
            Please select
        &lt;/option&gt;

        &lt;option&gt;A&lt;/option&gt;
        &lt;option&gt;B&lt;/option&gt;
        &lt;option&gt;C&lt;/option&gt;
    &lt;/select&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let app27 = new Vue({
    el: "#app-27",
    data: {
        selected: ""
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-27">
                                        <select v-model="selected">
                                            <option disabled
                                                    value="">Please select</option>
                                            <option>A</option>
                                            <option>B</option>
                                            <option>C</option>
                                        </select>
                                        <p>selected: {{selected}}</p>
                                    </div>
                                </samp>
                            </div>

                            <p>
                                <div class="alert-warning">
                                    如果<code>v-model</code>表达式的初始值未能匹配任何选项, select元素将被渲染为未选中状态.
                                    在ios中, 会导致用户无法选择第一个选项. 这种情况下, ios不触发change事件. 因此更推荐如上所示提供一个值为空,
                                    并且被disabled的选项
                                </div>
                            </p>

                            <p>
                                选择框多选时(使用multiple attribute), 让<code>v-model</code>绑定到一个数组上即可.
                            </p>

                            <br>

                            <p>
                                还可以在option元素上使用<code>v-for</code>指令进行动态渲染, 例如 :
                            </p>

                            <pre>&lt;div id="app-28"&gt;<br>  &lt;select v-model="selected"&gt;<br>    &lt;option v-for="option in options" v-bind:value="option.value"&gt;<br>      {{option.text}}<br>    &lt;/option&gt;<br>  &lt;/select&gt;<br>  &lt;p&gt;selected : {{selected}}&lt;/p&gt;<br>&lt;/div&gt;</pre>

                            <pre>
let app28 = new Vue({
    el: "#app-28",
    data: {
        selected: "one",
        options: [
            { text: "first", value: "one" },
            { text: "second", value: "two" },
            { text: "third", value: "three" }
        ]
    }
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="app-28">
                                        <select v-model="selected">
                                            <option v-for="option in options"
                                                    v-bind:value="option.value">
                                                {{option.text}}
                                            </option>
                                        </select>
                                        <p>selected : {{selected}}<p>
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="182">值绑定</h2>
                    <div>
                        <p>
                            对于单选按钮, 复选框以及选择框的选项, <code>v-model</code>绑定值通常为静态字符串, 数组(对于复选框可以是布尔值).
                            但是有时想要把值绑定到Vue实例的动态属性上, 这时可以用<code>v-bind</code>实现, 且这个属性的值可以不为字符串
                        </p>
                    </div>

                    <div>
                        <h4>复选框</h4>
                        <div>
                            <p>
                                单个复选框选中与未选中的值可以通过<code>true-value</code>和<code>false-value</code> attribute指定,
                                它们将用于更新<code>v-model</code>绑定的值, 而不会影响控件的<code>value</code> attribute
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>单选按钮</h4>
                        <div>
                            <pre>
&lt;div id="radio-dynamic-bind"&gt;
    &lt;input type="radio"
              v-model="picked"
              :value="dynamicValueA"&gt;
    &lt;input type="radio"
              v-model="picked"
              :value="dynamicValueA"&gt;
    &lt;p&gt;Picked : {{picked}}&lt;p&gt;
&lt;/div&gt;
                            </pre>
                            <pre>
let radioDynamicBind = new Vue({
    el: "#radio-dynamic-bind",
    data: {
            picked: '',
            dynamicValueA: 'foo',
            dynamicValueB: 'bar'
    }                          
}) 
                            </pre>
                            <div class="code-output">
                                <samp>
                                    <div id="radio-dynamic-bind">
                                        <input type="radio"
                                               v-model="picked"
                                               :value="dynamicValueA">
                                        <input type="radio"
                                               v-model="picked"
                                               :value="dynamicValueB">
                                        <p>Picked : {{picked}}</p>
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="183">修饰符</h2>

                    <div>
                        <h4>.lazy</h4>
                        <div>
                            <p>
                                在默认情况下，v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符，从而转变为使用 change
                                事件进行同步
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>.number</h4>
                        <div>
                            <p>
                                自动将用户输入值转化为数值类型, 若无法被<code>parseFloat()</code>解析, 则返回原始值
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>.trim</h4>
                        <div>
                            <p>
                                过滤输入的首尾空白
                            </p>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="184">在组件上使用v-model</h2>
                    <div>
                        <p>
                            HTML原生的输入元素类型并不总能满足需求. Vue组件系统允许我们创建具有完全自定义项行为且可复用的组件, 这些组件甚至可以与<code>v-model</code>一起使用
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="19">组件基础</h1>

                <article>
                    <h2 id="191">基本示例</h2>
                    <div>
                        <p>
                            组件是可复用的Vue实例, 且带名字. 可以在一个通过<code>new Vue</code>创建的Vue根实例中, 把这个组件作为自定义元素使用.
                            组件通过<code>Vue.component(componentName, options)</code>进行全局注册.
                            <code>template</code>选项是一个字符串定义了该组件的模板html代码, 例如 :
                        </p>

                        <pre>
Vue.component('button-counter', {
    data: function () {
        return {
            count: 0
        }
    },
    template: '&lt;button @click="count++"&lt;Clicked {{count}} times&lt;/button&gt;'
});
                        </pre>

                        <pre>&lt;button-counter id="app-30"&gt;&lt;/button-counter&gt;</pre>
                        <pre>let app30 = new Vue({el: "#app-30"}); 
// 必须创建根实例才能把组件当作自定义元素使用
                        </pre>

                        <div class="code-output">
                            <samp>
                                <button-counter id="app-30"></button-counter>
                            </samp>
                        </div>

                        <p>
                            因为组件是可复用的Vue实例, 其选项对象类似于创建根实例时的选项对象,
                            例如都拥有如<code>data</code>, <code>computed</code>等选项, 例外是<code>el</code>这样的根实例特有property
                        </p>

                        <br>

                        <p>
                            注意到Vue组件非常类似于<strong>自定义元素</strong> —— 它是<a
                               href="https://www.w3.org/wiki/WebComponents/">web组件规范</a>的一部分,
                            这是因为Vue的组件语法部分参考了该规范. 例如Vue组件实现了<a
                               href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md">slot
                                api</a> 和 <code>is</code> attribute.
                            但是有几个关键差别:
                        </p>

                        <ol>
                            <li>
                                Web Components规范已经完成并通过, 但未被所有浏览器原生实现. 相比之下, Vue组件不需要任何
                                <a data-toggle="tooltip"
                                   title="A polyfill is a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it.">
                                    polyfill
                                </a>. 必要时, Vue组件也可以包装在原生自定义元素之内.
                            </li>
                            <li>
                                Vue组件提供了纯自定义元素所不具备的一些功能, 或以自定义元素形式发布时, <a
                                   href="https://custom-elements-everywhere.com/#vue">仍然有很好的操作性</a>.
                                Vue CLI也支持将Vue组件构建成为原生的自定义元素.
                            </li>
                        </ol>

                        <p>
                            <code>Vue.component</code>为全局API, 第一参数类型为string作为全局注册名,
                            第二个参数类型为<code>Function</code>或<code>Object</code>， 具体用法如下
                        </p>

                        <pre>
&lt;!-- 注册组件, 传入一个扩展过的构造器 --&gt;
Vue.component('my-comp', Vue.extend( { /* ... */ } ))

&lt;-- 注册组件, 传入一个选项对象 (自动调用 Vue.extend) --&gt;
Vue.component('my-comp', {/* ... */})

&lt;-- 获取注册的组件 (始终返回构造器) --&gt;
let MyComp = Vue.component('my-comp');
                        </pre>
                    </div>
                </article>

                <article>
                    <h2 id="192">组件复用</h2>
                    <div>
                        <p>
                            组件是可复用的, 它们彼此间独立, 各自维护它们自己的数据. 可以看到定义组件时, <code>data</code>是个返回对象的函数而不是一个对象,
                            这是因为只有这样每个Vue实例才可以维护一份被返回对象的独立拷贝. 如果没有这条规则, 那么声明多个自定义元素, 他们之间的数据是共享的
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="193">组件组织</h2>
                    <div>
                        <p>
                            通常一个应用会以一颗嵌套的组件树的形式来组织. 组件又可能包含其他组件. 为了能够在模板中使用, 这些被别的组件使用的组件必须先注册以便Vue
                            能够识别. 有两种组件的注册类型 : <strong>全局注册(通过<code>Vue.component</code>)</strong>和<strong>局部注册</strong>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="194">通过Prop向子组件传递数据</h2>
                    <div>
                        <p>
                            通过<code>Vue.component</code>自定义组件时, 可以定义一个值为数组名为<code>props</code>的选项, 该数组
                            内的字符串表示该自定义组件被用作自定义元素的时候可以拥有的attribute, 或者称作该组件的prop, <code>props</code>数组的每一项称作prop.
                            结合<code>v-bind</code>使用, 可以为这些attribute实现动态数据绑定, 例如 :
                        </p>

                        <pre>
Vue.component("blog-post", {
    props: ["id", "title"],
    template: "&lt;h5&gt;blog id: {{id}}, blog title: {{title}}&lt;/h5&gt;"
});
                        </pre>

                        <pre>
&lt;div id="app-31"&gt;
    &lt;blog-post v-for="post in posts" 
                  :key="post.id" 
                  :id="post.id" 
                  :title="post.title"&gt;
    &lt;/blog-post&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app31 = new Vue({
    el: "#app-31",
    data: {
        posts: [
            { id: 1, title: "Vue" },
            { id: 2, title: "JQuery" },
            { id: 3, title: "React" }
        ]
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-31">
                                    <blog-post v-for="post in posts"
                                               :key="post.id"
                                               :id="post.id"
                                               :title="post.title"></blog-post>
                                </div>
                            </samp>
                        </div>

                        <br>

                        <p>
                            当组件越来越复杂, 其自定义元素所需要的attribute越来越多, 则需要向<code>props</code> 数组中加入更多prop, 这会变得很麻烦. 因此,
                            可以只在<code>props</code>中加入一个prop, 并用一个对象为该prop赋值, 其余信息通过该对象解析出来. 例如 :
                        </p>

                        <pre>
Vue.component("foo", {
    props: ["propobj"],
    template: "&lt;h5&gt;{{propobj.propA}}, {{propobj.propB}}&lt;/h5&gt;"
});   
                        </pre>

                        <pre>
&lt;div id="app-32"&gt;
    &lt;foo :propobj="obj"&gt;&lt;/foo&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app32 = new Vue({
    el: "#app-32",
    data: {
        obj: {
            propA: 'a',
            propB: 'b'
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-32">
                                    <foo :propobj="obj"></foo>
                                </div>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="195">单个根元素</h2>
                    <div>
                        <p>
                            <code>template</code> 选项中的html代码只能有一个根元素, 需要多个元素时可以将它们包裹在一个父元素内, 并用js模板字符串语法使模板更加易读
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="196">监听子组件事件</h2>
                    <div>
                        <p>
                            子组件可以通过在模板内使用子组件实例的<code>$emit</code>方法,
                            配合<code>v-on</code>指令 (因为指令的表达式可以直接访问实例的属性, 所以可以直接使用<code>$emit</code> ), 或者通过方法(需要靠
                            <code>this</code>才能访问到该方法)
                            等方式向父组件发出一个事件.
                        </p>

                        <p>
                            <code>$emit</code>属于实例方法/事件类, 第一个参数类型为字符串, 作为向父组件发出的事件名称.
                        </p>

                        <pre>
Vue.component("demo", { 
    template: `
        &lt;button @click="$emit('greeting')"&gt;click&lt;/button&gt;
    ` 
    }
);
                        </pre>

                        <pre>
&lt;div id="app-33"&gt;
    &lt;demo @greeting="sayHi"&gt;&lt;/demo&gt;
&lt;div&gt;
                        </pre>

                        <p>
                            要注意父组件(本例子中为根实例)只能通过该子组件监听它发出的事件, 以下代码无法正常监听:
                        </p>

                        <pre>
&lt;div id="app-33 @greeting="sayHi"&gt;
    &lt;demo&gt;&lt;/demo&gt;
&lt;div&gt;  
                        </pre>

                        <p>
                            还需要注意的是, 因为子组件在父组件的模板中被使用(本例中根实例没有提供模板, 所以Vue把挂载元素的外部html当作模板), 所以子组件的指令表达式会在父组件中查值,
                            也就是说在该例子中, 我们应该把<code>sayHi</code>方法定义到根实例上而不是子组件<code>demo</code>上.
                        </p>

                        <pre>
let app33 = new Vue({
    el: "#app-33",
    methods: {
        sayHi() {
            alert('Hello, Vue');
        }
    }
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-33">
                                    <demo @greeting="sayHi"></demo>
                                </div>
                            </samp>
                        </div>
                    </div>

                    <div>
                        <h4>使用事件抛出一个值</h4>
                        <div>
                            <p>
                                有时用事件抛出一个值使非常有用的, 值可以通过第二个<code>$emit</code>的第二个参数指定,
                                然后侦听这个事件的父组件通过<code>$event</code>访问到抛出的值, 例如 :
                            </p>

                            <pre>
Vue.component("demo2", { 
    template: `
        &lt;button @click="$emit('greeting', 'Hello, Vue')"&gt;
            click
        &lt;/button&gt;
        ` 
    }
);
                            </pre>

                            <pre>
&lt;demo2 id="app-34"
          @greeting="sayHi($event)"&gt;&lt;/demo2&gt;
                            </pre>

                            <pre>
let app34 = new Vue({
    el: "#app-34",
    methods: {
        sayHi(message) {
            alert(message);
        }
    }
});
                            </pre>


                            <div class="code-output">
                                <samp>
                                    <demo2 id="app-34"
                                           @greeting="sayHi($event)"></demo2>
                                </samp>
                            </div>
                        </div>
                    </div>

                    <div>
                        <h4>在组件上使用v-model</h4>
                        <div>
                            <p>
                                自定义事件也可以用于创建支持<code>v-model</code>的自定义输入组件.
                                当用在组件上时, <code>v-model</code>的行为会与默认不同 :
                            </p>

                            <pre>
&lt;custom-input v-model="searchText"&gt;&lt;/custom-input&gt;
                            </pre>

                            <p>
                                等价于 :
                            </p>

                            <pre>
&lt;custom-input v-bind:value="searchText" 
                 v-on:input="searchText = $event"&gt;&lt;/custom-input&gt;
                            </pre>

                            <p>
                                为了让组件正常工作, 组件内的input元素必须 : <br>
                                <ul>
                                    <li>将其value attribute绑定到一个名叫value的prop上</li>
                                    <li>在其input事件被触发时, 将新的值通过自定义的input事件抛出</li>
                                </ul>
                                <p>完整地写成代码后应该有如下形式 : </p>
                            </p>

                            <pre>
Vue.component("custom-input", {
    props: ["value"],
    template: `
        &lt;input :value="value" 
               @input="$emit('input', $event.target.value)"&gt;
    `
});
                            </pre>

                            <pre>
&lt;div id="custom-input-demo"&gt;
    &lt;custom-input v-model="message"&gt;&lt;/custom-input&gt;
    &lt;p&gt;Message: {{ message }}&lt;/p&gt;
&lt;/div&gt;
                            </pre>

                            <pre>
let customInputDemo = new Vue({
    el: "#custom-input-demo",
    data: {
        message: "",
    }
})
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <div id="custom-input-demo">
                                        <custom-input v-model="message"></custom-input>
                                        <p>Message: {{message}}</p>
                                    </div>
                                </samp>
                            </div>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="197">通过插槽分发内容</h2>
                    <div>
                        <p>
                            经常需要向组件传递内容(content), 向组件中添加content只需要在定义组件时向模板template中加入Vue自定义的slot元素
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="198">动态组件</h2>
                    <div>
                        <p>
                            Vue提供了一个元素component和一个attribute <code>is</code>来实现组件的切换, <code>is</code>的值为一个已经注册的组件名,
                            或一个组件的options对象
                        </p>
                        <p>
                            这个<code>is</code> attribute也可以用于常规HTML元素, 但这些元素被视为组件, 意味着所有的attribute都会作为DOM attribute被绑定.
                            对于像<code>value</code>这样的property, 若想让其如预期工作, 需要用<code>.prop</code>修饰器.
                        </p>
                        <p>
                            <code>component</code>为Vue的内置组件, 可用prop为 <code>is</code> 和 <code>inline-template</code>,
                            用于渲染一个"元组件" 为动态组件.
                        </p>
                    </div>
                </article>


                <article>
                    <h2 id="199">解析DOM模板时的注意事项</h2>
                    <div>
                        <p>
                            有些HTML元素对于哪些元素可以出现在其内部有严格限制, 有些元素只能出现在特定元素内部(例如ul内只能出现li, table内只能使用table相关元素).
                            这会为自定义组件的使用带来麻烦.
                            这时可以通过提供一个正确的元素再使用<code>is</code> attribute将其替换为所需自定义组件.
                        </p>
                        <p>需要注意, 如果从以下来源使用模板, 这条限制不存在 : </p>
                        <ul>
                            <li>字符串(例如
                                <code>templte: '&lt;ul&gt; &lt;my-component&gt;&lt;/my-component&gt;&lt;/ul&gt;'</code>)
                            </li>
                            <li>单文件组件 (<code>.vue</code>)</li>
                            <li><code>&lt;script type="text/x-template"&gt;</code></li>
                        </ul>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="21">组件注册</h1>

                <article>
                    <h2 id="211">注册名</h2>
                    <div>
                        <p>
                            <ul>
                                <li>kebab-case ： 短横线分隔命名</li>
                                <li>PascalCase : 首字母大写. 只在模板中可用(模板中也可以用等价kebab-case), DOM中必须使用等价kebab-case</li>
                            </ul>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="212">全局注册</h2>
                    <div>
                        <p>
                            通过<code>Vue.component</code>注册. 全局注册的组件意味着它们在注册后可以用在任何新创建的Vue根实例的模板中. 所有子组件也可以互相使用
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="213">局部注册</h2>
                    <div>
                        <p>
                            全局注册往往是不够理想的。比如，如果你使用一个像 webpack
                            这样的构建系统，全局注册所有的组件意味着即便你已经不再使用一个组件了，它仍然会被包含在你最终的构建结果中。这造成了用户下载的
                            JavaScript 的无谓的增加。
                        </p>

                        <br>

                        <p>
                            这种情况下, 可以通过一个普通js options对象定义组件. 例如 :
                        </p>
                        <pre>let ComponentA= {/* ... */};<br>let ComponentB = {/* ... */};</pre>

                        <br>

                        <p>
                            然后在<code>components</code>选项中定义想要使用的组件 :
                        </p>

                        <pre>&lt;div id="app-35"&gt;<br>  &lt;component-a&gt;&lt;/component-a&gt;<br>  &lt;component-b&gt;&lt;/component-b&gt;<br>&lt;/div&gt;</pre>

                        <pre>
let app35 = new Vue({
    el: "#app-35", 
    components: {
        'component-a': ComponentA,
        'component-b': ComponentB}
    }
);
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-35">
                                    <component-a></component-a>
                                    <component-b></component-b>
                                </div>
                            </samp>
                        </div>

                        <p>
                            对于<code>components</code>对象的每个属性来说, 属性名即自定义元素名, 属性值即组件的options对象.
                        </p>

                        <p>
                            注意<strong>局部注册的组件在其子组件中不可用</strong> :
                        </p>

                        <pre>
components: {
    'component-a': componentA,
    ComponentB // 简写语法, 等价于 ComponentB: ComponentB
},
template: `
    &lt;component-a&gt;
        &lt;!-- error --&gt;
        &lt;component-b&gt;&lt;/component-b&gt;
    &lt;/component-a&gt;
`
                        </pre>

                        <p>
                            如果希望 <code>ComponentA</code>, 在<code>ComponentB</code>可用,
                            需要在<code>ComponentB</code>的<code>components</code>选项中指明使用<code>ComponentA</code> :
                        </p>

                        <pre>
let ComponentB = {
    // ......

    components: {
        'component-a': ComponentA
    }
}
                        </pre>

                        <p>
                            <code>components</code>选项属于选项/资源类, 指明了组件能使用的子组件.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="214">模块系统</h2>
                </article>
            </section>

            <section>
                <h1 id="22">Prop</h1>

                <article>
                    <p>
                        <code>props</code>属于选项/数据, 类型为<code>Array&lt;string&gt; | Object</code>,
                        定义了组件在作为自定义元素使用时可以用的attribute, 这些attribute被称作组件的prop.

                        prop可以看作子组件暴露给父组件的接口, 给予父组件与子组件通信的能力. 由于根组件(new Vue创建)没有父组件,
                        所以多数情况下给根组件定义<code>props</code>选项是没有意义的.
                    </p>

                    <p>
                        但是有时候为了测试, 可能会想给根实例定义prop, 这时我们可以使用Vue提供的<code>propsData</code>选项,
                        它属于选项/数据类, 类型为 <code>{[key: string] : any}</code>,
                        其键为prop名, 值为传入该prop的值
                        该选项被限制为只能用于 <code>new</code>创建的实例中(不一定只是 <code>new Vue</code>,
                        可以是任意继承了<code>Vue</code>的类所<code>new</code>出来的实例 ), 例如 :
                    </p>

                    <pre>
let propsDataDemo = new Vue({
  el: "#props-data-demo",
  props: ['propA', 'propB'],
  propsData: {
    propA: "foo",
    propB: "bar"
  },
  template: `
    &lt;p>{{ propA }} {{ propB }}&lt;/p>
  `
})
                    </pre>

                    <pre>
&lt;div id="props-data-demo"&gt;&lt;/div&gt;
                    </pre>

                    <div class="code-output">
                        <samp>
                            <div id="props-data-demo"></div>
                        </samp>
                    </div>

                    <p>
                        阅读完这一章, 我们就掌握了全部选项/数据 :
                    </p>
                    <ul>
                        <li>data</li>
                        <li>computed</li>
                        <li>methods</li>
                        <li>watch</li>
                        <li>props</li>
                        <li>propsData</li>
                    </ul>

                </article>

                <article>
                    <h2 id="221">Prop的大小写</h2>
                    <div>
                        <p>
                            HTML的attribute名是大小写不敏感的, 所以浏览器会把所有大写字符解释成小写. 意味着当使用DOM模板时,
                            camelCase的prop名需要使用其等价的kebab-case命名, 而<code>template</code>中使用该prop不需要此限制.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="222">Prop类型</h2>
                    <div>
                        <p>
                            <code>props</code>可以不只是一个数组, 它还可以是一个对象, 其每个属性以及属性值表示自定义组件的attribute name和该attribute的值类型. 例如
                            :
                        </p>
                        <pre>props: {title: String, likes: Number, isPublisheed: Boolean, commentIds: Array, author: Object, callback: Function, contactsPromise: Promise}</pre>
                    </div>
                </article>

                <article>
                    <h2 id="223">传递静态或动态Prop</h2>
                    <div>
                        <p>
                            为prop传静态值时, 传入的值为字符串类型, 要想传入任意类型, 需要使用<code>v-bind</code>, 例如 :
                        </p>
                        <pre>v-bind:likes="42"</pre>
                        <p>
                            即使42是静态的, 但仍然需要使用<code>v-bind</code>指令来告诉Vue使用引号内的js表达式而不是将整个attribute值作为字符串使用
                        </p>

                        <br>

                        <p>
                            可以在表达式中做各种运算来得到动态prop, 例如 :
                        </p>
                        <pre>v-bind:title="post.title + ' by ' + post.author.name"</pre>
                        <pre>
data: {
    post: {
        title: "Hello Vue", 
        author: {
            name: "zan", 
            gender: "male"
        }
    }
}
                        </pre>

                        <br>

                        <p>
                            如果想要将对象的所有属性作为prop传入, 则可以使用不带参数的<code>v-bind</code>. 例如 :
                        </p>
                        <pre>post: {id: 1, title: 'Learn Vue'}</pre>
                        <p>
                            则以下模板 :
                        </p>
                        <pre>&lt;blog-post :="post"&gt;&lt;/blog-post&gt;</pre>
                        <p>等价于</p>
                        <pre>&lt;blog-post :id="post.id" :title="post.title"&gt;&lt;/blog-post&gt;</pre>
                    </div>
                </article>

                <article>
                    <h2 id="224">单向数据流</h2>
                    <div>
                        <p>
                            所有的prop使得父子prop之间形成了一个<strong>单向下绑定</strong> : 父级prop的更新会流动到子组件, 但反过来却不行.
                            这样会防止从子组件意外改变父级组件的状态, 但导致应用的数据流向难以理解
                        </p>
                        <p>
                            额外的, 每次父级组件发生更新时, 子组件中所有的prop会被刷新为最新值. 这意味着不应该在子组件内部改变prop. 事实上,
                            我们应该避免直接在组件中修改组件prop, 即使传向该prop的数据与父组件没有任何关系,
                            例如 <code>v-bind:prop="123"</code> , 因为prop是父组件向子组件通信的方式, 信息的统治权掌握在父组件手上,
                            子组件自己本身并不知道自己的prop绑定的数据来源, 并且, 当父组件重新渲染时, 子组件的prop也会被重写. 如果我们尝试直接在组件内修改prop,
                            Vue会在浏览器控制台中发出警告 :
                        </p>
                        <div class="alert-danger">
                            Avoid mutating a prop directly since the value will be overwritten whenever the parent
                            component re-renders. Instead, use a data or computed property based on the prop's value.
                        </div>

                        <pre>
Vue.component("child0", {
    props: ['childProp'],
    methods: {
        change() {
            this.childProp = Math.round(Math.random() * 100);
        }
    },
    template: `
        &lt;div&gt;
            &lt;button&gt; type="button" 
                    @click="change"&gt;
                Change from child
            &lt;/button&gt;
            Child: {{childProp}}
        &lt;/div&gt;
    `
});
                        </pre>

                        <pre>
&lt;div id="app-36"&gt;
    &lt;button @click="change"&gt;Change by parent&lt;/button&gt;
    Parent: {{ num }}
    &lt;child0 :child-prop="num"&gt;&lt;/child0&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app36 = new Vue({
    el: "#app-36",
    data: {
        num: 0
    },
    methods: {
        change() {
            this.num = Math.round(Math.random() * 100);
        }
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-36">
                                    <button @click="change">Change by parent</button>
                                    Parent: {{ num }}
                                    <child0 :child-prop="num"></child0>
                                </div>
                            </samp>
                        </div>

                        <br>

                        <p>
                            有两种尝试改变一个prop的情形 :
                        </p>

                        <p>
                            <strong>1. 这个父组件传递给子组件的prop一个初始值; 这个子组件接下来希望将其作为本地的prop数据使用.</strong> 这种情况下,
                            最好定义一个本地的data属性并将这个prop用作其初始值. 例如 :
                        </p>

                        <pre>
Vue.component("child1", {
    props: ['initialCounter'],
    data: function () {
        return {
            counter: this.initialCounter
        }
    },
    template: `
        &lt;div>
            &lt;p>Child prop initialCounter : {{initialCounter}}&lt;/p>
            &lt;button @click="counter++">Change local counter by child&lt;/button>
            &lt;p>Child local counter : {{ counter }}&lt;/p>
        &lt;/div>
    `
})
                        </pre>

                        <pre>
&lt;div id="app-37"&gt;
    &lt;button @click="initialCounter++">Change initialCounter by parent&lt;/button>
    &lt;child1 :initial-counter="initialCounter">&lt;/child1>
&lt;/div>
                        </pre>

                        <pre>
let app37 = new Vue({
    el: "#app-37",
    data: {
        initialCounter: 0
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-37">
                                    <button @click="initialCounter++">Chnage initialCounter by parent</button>
                                    <child1 :initial-counter="initialCounter"></child1>
                                </div>
                            </samp>
                        </div>

                        <p>
                            <strong>2. 这个prop以一种原始的值传入且需要进行转换. </strong>这种情况下, 最好使用这个prop的值来定义一个计算属性. 例如 :
                        </p>

                        <pre>
Vue.component("child2", {
    props: ['charset'], 
    computed: {
        dataCharset: function() { 
            return this.charset.trim().toLowerCase() 
        } 
    }, 
    template: `&lt;p&gt;Computed dataCharset : {{dataCharset}}&lt;/p&gt;` 
});
                        </pre>

                        <pre>
&lt;child2 id="app-38" charset=" UtF-8 "&gt;
&lt;/child2&gt;
                        </pre>

                        <pre>
let app38 = new Vue({
    el: "#app-38"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <child2 id="app-38"
                                        charset=" UtF-8  "></child2>
                            </samp>
                        </div>

                        <p>
                            需要注意的是js的对象和数组是通过引用传递的, 所以对于数组或对象类型的prop来说, 子组件改变该对象或数组将会影响到父组件的状态.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="225">Prop验证</h2>
                    <div>
                        <p>
                            可以为组件的prop指定验证要求. 为了定制prop的验证方式, 可以为<code>props</code>中的值提供一个带有验证需求的对象, 而不是数组.
                        </p>
                        <p>
                            对象的property name为组件的prop name, 属性值可以为以下类型 :
                            <ul>
                                <li>任意原生js constructor function (String, Number, Boolean, Array, Object, Date, Function,
                                    Symbol) 和自定义的constructor function(Vue 会自动进行instanceof检测)</li>
                                <li>数组, 其中项为 constructor function， 指明了该attribute可以接受的类型范围</li>
                                <li>对象, 其中<code>type</code> property的值可以为以上两种类型值, <code>required</code>
                                    property指明该prop是否是必须的, <code>default</code> property
                                    指出该attribute的默认值(数组和对象默认值必须从一个工厂函数获取, 即<code>default</code> 值为一个函数),
                                    <code>validator</code> property为自定义验证property, 值为一个predicate</li>
                            </ul>
                        </p>

                        <pre>
Vue.component("app39", {
    props: {
        propA: Number,
        propB: [String, Date],
        propC: {
            type: Function,
            required: true,
        },
        propD: {
            type: [Object, Array],
            default: () => [1, 1, 4, 5, 1, 4]
        },
        propE: {
            type: Number,
            validator: value => value >= 0
        }
    },
    template: `
        &lt;div>
            &lt;p>{{propA}}&lt;/p>
            &lt;p>{{propB}}&lt;/p>
            &lt;p>{{propC}}&lt;/p>
            &lt;p>{{propD}}&lt;/p>
            &lt;p>{{propE}}&lt;/p>
        &lt;/div>
    `
})
                        </pre>

                        <pre>
&lt;app39 id="app-39"
       :prop-a="2020"
       :prop-b="new Date()"
       :prop-c="function() {}"
       :prop-d="[0, 1, 2, 3]"
       :prop-e="10">&lt;/app39>
                        </pre>

                        <pre>
let app39 = new Vue({
    el: "#app-39"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <app39 id="app-39"
                                       :prop-a="2020"
                                       :prop-b="new Date()"
                                       :prop-c="function() {}"
                                       :prop-d="[0, 1, 2, 3]"
                                       :prop-e="10"></app39>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="226">非Prop的Attribute</h2>
                    <div>
                        <p>
                            一个非prop的attribute是指传向一个组件, 但是该组件并没有相应prop定义的attribute.
                        </p>
                        <p>
                            因为显式定义的prop适用于向子组件传入信息, 然而组件库的作者并不总能预见组件会被用于什么场景. 这就是为什么组件可以接受任意attribute,
                            而这些attribute会被添加到这个组件的根元素上, 例如 :
                        </p>
                        <pre>Vue.component("custom-link", {template: "&lt;a&gt;custom-link&lt;/a&gt;"});</pre>
                        <pre>&lt;custom-link id="app-37" href="https://cn.vuejs.org/"&gt;&lt;/custom-link&gt;</pre>
                        <pre>let app37 = new Vue({el: "#app-37"});</pre>

                        <div class="code-output">
                            <samp>
                                <custom-link id="app-40"
                                             href="https://cn.vuejs.org/"></custom-link>
                            </samp>
                        </div>

                        <br>

                        <p>
                            <code>href</code> attribute将会被添加到<code>template</code>的根元素<code>&lt;a&gt;</code>上.
                        </p>
                    </div>

                    <div>
                        <h4>替换/合并已有的attribute</h4>
                        <div>
                            <p>模板内已经有的attribute会被组件提供的对应attribute值所替换掉, 所幸, <code>class</code> 和 <code>style</code>
                                attribute采取的是合并的策略, 从而得到最终值</p>
                        </div>
                    </div>

                    <div>
                        <h4>禁用attribute继承</h4>
                        <div>
                            <p>如果不想组件的根元素继承非prop的attribute, 可以在组件的option中设置<code>inheritAttrs: false</code>, 例如 : </p>

                            <pre>Vue.component('demo', {inheritAttrs: false, /* ... */})</pre>

                            <p>
                                <code>inheritAttrs</code>属于选项/其他, 类型为<code>boolean</code>, 默认值为<code>true</code>.
                            </p>

                            <p>
                                这尤其配合实例的<code>$attrs</code>. <code>$attrs</code>属于实例属性, 储存了所有实例上所有的非prop的attribute (
                                <code>class</code>和<code>style</code>除外 ).
                            </p>
                            <p>
                                有了 <code>inheritAttrs: false</code> 和 <code>$attrs</code>, 我门就可以手动决定这些
                                attribute会被赋予模板<code>template</code>内的哪个元素.
                            </p>
                            <div class="alert-warning">
                                注意<code>inheritAttrs: false</code>不影响<code>style</code>和<code>class</code>的绑定
                            </div>
                        </div>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="23">自定义事件</h1>

                <article>
                    <code>v-on</code>指令预期值为<code>Function | Inline Statement | Object</code>,
                    用在普通元素上只能监听<strong>原生DOM事件</strong>. 用在自定义元素上, 可以监听子组件通过 <code>$emit</code>
                    发出的<strong>自定义事件</strong>
                </article>

                <article>
                    <h2 id="231">事件名</h2>
                    <div>
                        <p>
                            不同于组件和prop, 事件名不存在任何自动化的大小写转换. 但<code>v-on</code>指令参数会被转化为小写(因为HTML大小写不敏感), 所以,
                            例如<code>v-on:myEvent</code>会变成<code>v-on:myevent</code> —— 导致<code>myEvent</code>无法监听到.
                            因此始终推荐用kebab-case事件名
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="232">自定义组件的v-model</h2>
                    <div>
                        <p>
                            一个组件上的<code>v-model</code>指令默认利用名为<code>value</code>的prop和名为<code>input</code>的事件, 但是像单选,
                            复选框等类的输入控件可能将 <code>value</code> attribute用于不同目的. 组件的<code>model</code> option可以避免冲突.
                            <code>model</code>为一个对象, 有<code>prop</code>和<code>event</code>两个属性(值为字符串),
                            分别指出<code>v-model</code>指令值所绑定的attribute名和事件名. 例如 :
                        </p>

                        <pre>
Vue.component('demo', {
    model: {
        prop: 'myProp',
        event: 'myEvent'
    }
})
                        </pre>

                        <p>
                            那么使用该组件时 :
                        </p>

                        <pre>
&lt;demo v-model="xxx"&gt;&lt;/demo&gt;
                        </pre>

                        <p>等价于 : </p>

                        <pre>
&lt;demo :myProp="xxx" @myEvent="xxx = $event"&gt;&lt;/demo&gt;
                        </pre>


                        <p>
                            注意, <code>model</code>中的<code>prop</code>仍需要在<code>props</code>中声明.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="233">将原生事件绑定到组件</h2>
                    <div>
                        <p>
                            可能需要在一个组件的根元素上直接监听一个原生事件. 这时, 可以用<code>v-on</code>指令加上其<code>.native</code>修饰符, 例如 :
                        </p>

                        <pre>&lt;base-input v-on:focus.native="onFocus"&gt;&lt;/base-input&gt;</pre>

                        <p>
                            但根元素不总是一个<code>input</code>元素, 这时, 父级 <code>.native</code>监听器将静默失败,
                            <code>onFocus</code>处理函数也不会如期调用.
                        </p>
                        <p>
                            为解决这一问题, Vue提供了一个实例属性<code>$listeners</code> property, 它是一个对象, 包含了作用在这个组件上的所有监听器. 例如 :
                        </p>
                        <pre>{focus: function (event) {}, input: function (value) {}}</pre>
                        <br>
                        <p>
                            有了这个<code>$listeners</code> 属性, 就可以配合<code>v-on="$listeners"</code> (类似于v-bind的无参数形式)
                            将所有的事件监听器指向这个组件的某个特定子元素.
                            对于类似<code>&lt;input&gt;</code>且希望配合<code>v-model</code>的组件来说, 为这些监听器创建一个类似如下的
                            <code>inputListeners</code>计算属性通常非常有用 :
                        </p>

                        <pre>
computed: {
    inputListeners: function () {
        let vm = this; 
        return Object.assign({}, this.$listeners, {
            input: function (event) {
                vm.$emit('input', event.target.value)
            }
        })
    }
}
                        </pre>

                        <p>
                            例子 :
                        </p>

                        <pre>
Vue.component('app42', {
    inheritAttrs: false,
    props: ['label', 'value'],
    computed: {
        inputListeners: function () {
            let vm = this;

            return Object.assign({},
                this.$listeners,
                {
                    input: function (event) {
                        // 这将会与已有的input事件处理器合并成一个数组
                        vm.$emit('input', event.target.value)
                    }
                }
            )
        }
    },
    template: `
        &lt;label>
            {{label}}
            &lt;input
                v-bind="$attrs"
                :value="value"
                v-on="inputListeners"/>
            &lt;p>Value : {{value}}&lt;/p>
        &lt;/label>
    `
})
                        </pre>

                        <pre>
&lt;div id="app-42">
    &lt;p>Message: {{message}}&lt;/p>
    &lt;app42 type="text"
            label="Input"
            placeholder="placeholder"
            @change="onChange"
            @focus="onFocus"
            @input="onInput"
            v-model="message">
    &lt;/app42>
&lt;/div>
                        </pre>

                        <pre>
let app42 = new Vue({
    el: "#app-42",
    data: {
        message: "hello vue"
    },
    methods: {
        onChange: function (event) {
            console.log("on change : " + event);
        },
        onFocus: function (event) {
            console.log("on focus : " + event);
        },
        onInput: function (value) {
            console.log("on input : " + value);
        }
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-42">
                                    <p>Message: {{message}}</p>
                                    <app42 type="text"
                                           label="Input"
                                           placeholder="placeholder"
                                           @change="onChange"
                                           @focus="onFocus"
                                           @input="onInput"
                                           v-model="message">
                                    </app42>
                                </div>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="234">.syn修饰符</h2>
                    <div>
                        <p>
                            某些情况下需要对prop进行双向绑定, 但是真正的双向绑定会带来维护问题, 因为子组件可以修改父组件, 且在父组件和子组件都没有明显改动来源.
                        </p>
                        <p>
                            所以推荐以<code>update:myPropName</code>的模式触发事件取而代之.
                            我们通过<code>$emit</code>方法向父组件发出一个具有<code>updata:myPropName</code>这样的模式的事件, 以向父组件表达更新prop的意图.
                            例如 :
                        </p>

                        <pre>
Vue.component("child3", {
    props: {
        prop: Number
    },
    methods: {
        click: function () {
            this.$emit("update:prop", Math.random());
        }
    },
    template: `
        &lt;div>
            &lt;p>child prop : {{prop}}&lt;/p>
            &lt;button @click="click">sync change from child component&lt;/button>
        &lt;/div>
    `
})
                        </pre>

                        <p>
                            随后, 我们可以在父组件中监听该事件, 并做出更新 :
                        </p>

                        <pre>
&lt;div id="app-43"&gt;
    &lt;child3 :prop="prop" @update:prop="prop = $event"&gt;&lt;/child3&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app43 = new Vue({
    el: "#app-43",
    data: {
        prop: Math.random()
    }
})
                        </pre>

                        <p>为了方便起见, 提供了<code>.sync</code>修饰符作为缩写, 例如 : </p>

                        <pre>
&lt;div id="app-43"&gt;
    &lt;child3 :prop.sync="prop"&lt;/child3&gt;
&lt;/div&gt;
                        </pre>

                        <p>
                            该修饰符会自动监听子组件发出的<code>updata:prop</code>请求, 并把<code>prop</code>更新为<code>$event</code>
                        </p>

                        <div class="code-output">
                            <samp>
                                <div id="app-43">
                                    <child3 :prop.sync="prop"></child3>
                                </div>
                            </samp>
                        </div>

                        <div class="alert-warning">
                            注意带该修饰符的<code>v-bind</code>的值不能是表达式, 只能是想要绑定的property名, 类似于<code>v-model</code>
                        </div>

                        <p>
                            当用一个对象同时设置多个prop时, 也可以将该修饰符和<code>v-bind</code>配合使用 :
                        </p>
                        <pre>v-bind.sync="propObj"</pre>

                        <br>

                        <p>
                            这样会把<code>propObj</code>对象中的每一个property都作为独立的prop传进去, 然后各自添加用于更新的<code>v-on</code>监视器
                        </p>

                        <br>

                        <div class="alert-warning">
                            <code>v-bind.sync</code>由于很多边缘情况考虑无法使用字面量对象赋值
                        </div>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="24">插槽</h1>

                <article>
                    <h2 id="241">插槽内容</h2>
                    <div>
                        <p>
                            Vue实现了一套内容分发(content distribution)的api, 灵感来自 <a
                               href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md">Web
                                Components规范草案</a>,
                            将<code>&lt;slot&gt;</code>元素作为承载分发内容的出口.
                        </p>
                        <p>
                            若要为自定义元素添加内容, 需要将<code>&lt;slot&gt;&lt;/slot&gt;</code>添加组件模板中, 使用组件时,
                            其内容替换掉<code>slot</code>元素. 如果模板内没有使用<code>slot</code>元素, 那么组件的内容会被抛弃.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="242">编译作用域</h2>
                    <div>
                        <p>
                            当我们想在插槽中使用数据时 :
                        </p>
                        <pre>
Vue.component('app44', { 
    props: ['prop'], 
    template: `
        &lt;p&gt;
            &lt;slot&gt;{{ prop.a }}&lt;/slot&gt;
        &lt;/p&gt;
        `
})
                        </pre>
                        <p>
                            上面这个slot的后备内容可以直接访问到组件的prop, 因为这个slot定义在该组件的模板中
                        </p>

                        <pre>&lt;app44 id="app-44" :prop={a: 'a', b: 'b', c: 'c'}&gt;{{prop.c}}&lt;/app44;&gt;</pre>
                        <p>但是这里app44元素的content只能访问根实例的数据, 因为它们会作为根实例模板被编译, 所以这里会报错, 除非使用准备好的后备内容
                            <code>{{ prop.a }}</code> : </p>

                        <pre>
&lt;app44 id="app-44" :prop={a: "a", b: "b", c: "c"}&gt;&lt;/app44&gt;
                        </pre>

                        <pre>
let app44 = new Vue({
  el: "#app-44",
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <app44 id="app-44"
                                       :prop="{a: 'a', b: 'b', c: 'c'}"></app44>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="243">后备内容</h2>
                    <div>
                        <p>
                            在<code>slot</code>元素内添加内容可以为其提供后备(fallback)内容, 这个内容只会在组件没有向插槽分发内容的时候被渲染, 就跟上个例子一样.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="244">具名插槽</h2>
                    <div>
                        <p>
                            有时在模板内需要多个插槽来放在不同的位置， 对于这种情况, 可以为<code>slot</code>元素提供一个<code>name</code> attribute.
                            一个不带<code>name</code>的<code>slot</code>会带有隐含名字"default"
                        </p>

                        <p>
                            在向具名插槽提供内容时, 其内容需要被包裹在一个<code>template</code>元素上并使用<code>v-slot</code>指令并提供参数,
                            参数为具名插槽的name值,
                            这样该template元素的内容就会用于替换其对应的具名插槽. 任何没有<code>v-slot</code>指令的template元素的内容会被视作默认插槽的内容. 然而,
                            如果希望明确指出默认插槽, 可以通过 <code>v-slot:default</code>实现.
                        </p>

                        <div class="alert-warning">注意<code>v-slot</code>只能用在template元素上</div>

                        <br>

                        <p>
                            同时, Vue还提供了<code>vm.$slots</code>实例属性,
                            类型为<code>{ [name: string]: ?Array&lt;VNode&gt; }</code>,
                            用来访问被插槽分发的内容, 每个具名插槽在这个对象中有其对应的属性
                            (例如: <code>&lt;template v-slot:foo&gt;&lt;/template&gt;</code>中的内容会在
                            <code>vm.$slots.foo</code> 中找到). <code>default</code>属性包含了默认插槽的内容.
                            注意, 该<code>vm</code>指的是定义了插槽的组件本身, 而不是向该组件插槽分发内容的父组件.
                        </p>

                        <p>
                            例子 :
                        </p>

                        <pre>
Vue.component('NamedSlotDemo', {
  template: `
    &lt;div>
      &lt;p>
        &lt;slot>&lt;/slot>
      &lt;/p>
      
      &lt;p>
        &lt;slot name="foo">{{ $slots.default[0].text }}&lt;/slot>
      &lt;/p>
    &lt;/div>
  `
})

let namedSlotDemo = new Vue({
    el: "#named-slot-demo"
})
                        </pre>

                        <pre>
&lt;named-slot-demo id="named-slot-demo"&gt;
    &lt;template v-slot:default&gt;
        default
    &lt;/template&gt;

    &lt;template v-slot:foo&gt;
        &lt;!-- 这里是根组件的作用域, 我们应该调用的是子组件的$slots, 而不是根组件的$slots --&gt;
    &lt;/template&gt;
&lt;/named-slot-demo&gt;
                        </pre>

                        <div class="code-output">
                            <samp>
                                <named-slot-demo id="named-slot-demo">
                                    <template v-slot:default>
                                        default
                                    </template>

                                    <template v-slot:foo>
                                    </template>
                                </named-slot-demo>
                            </samp>
                        </div>

                        <p>
                            在使用渲染函数书写一个组件时，访问 <code>vm.$slots</code> 最有帮助。
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="245">作用域插槽</h2>
                    <div>
                        <p>
                            由于编译作用域的原因, 我们无法在父组件模板中向子组件的插槽分发子组件的数据, 为了解决这个问题, 我们可以使用作用域插槽(scoped slot),
                            只需要为<code>v-slot</code>指令添加一个值作为插槽props的名字, 这样, 在父组件中,
                            我们就获得了子组件插槽通过<code>v-bind</code>所绑定的插槽props对象, 该对象的属性名为插槽prop的名字.
                        </p>
                        <p>
                            例如 :
                        </p>

                        <pre>
Vue.component('app45', {
    props: ['prop'], 
    template: `
        &lt;p&gt;
            &lt;!--  插槽prop中的 prop绑定了组件app45 props中的 prop --&gt;
            &lt;slot :prop="prop"&gt;{{prop.a}}&lt;/slot&gt;
        &lt;/p&gt;
    ` 
})
                        </pre>

                        <pre>&lt;app45 id="app-45" :prop="{a: 'a', b: 'b', c: 'c'}"&gt;<br>  &lt;template v-slot:default="slotProp"&gt;{{slotProp.prop.b}}&lt;/templte&gt;<br>&lt;/app45&gt;</pre>

                        <div class="code-output">
                            <samp>
                                <app45 id="app-45"
                                       :prop="{a: 'a', b: 'b', c: 'c'}">
                                    <template v-slot:default="slotProp">
                                        {{slotProp.prop.b}}
                                    </template>
                                </app45>
                            </samp>
                        </div>

                    </div>

                    <div>
                        <h4>独占默认插槽的缩写语法</h4>
                        <div>
                            <p>
                                上述情况下, 当被提供的内容只有默认插槽时, 该插槽被称作独占默认插槽(lone default slot).
                                组件标签可以直接使用v-slot attribute来定义插槽slot的名称, 而不必在组件内容处用<code>template</code>分发内容.
                            </p>

                            <pre>
&lt;app45 id="loneDefaultSLotDemo" 
          :prop="{a: 'a', b: 'b', c: 'c'}" 
          v-slot:default="slotProps"&gt;
    {{slotProps.prop.c}}
&lt;/app45&gt;
                            </pre>

                            <pre>
let loneDefaultSlotDemo = new Vue({
  el: "#loneDefaultSlotDemo",
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <app45 id="loneDefaultSlotDemo"
                                           :prop="{a: 'a', b: 'b', c: 'c'}"
                                           v-slot:default="slotProps">
                                        {{slotProps.prop.c}}
                                    </app45>
                                </samp>
                            </div>

                            <p>
                                这种写法还可以更简单, 省略掉参数, 直接指向默认slot. 注意默认slot的缩写语法不能跟具名slot混用, 因为这会导致作用域不明 :
                            </p>

                            <pre>
&lt;demo v-slot="slotProps"&gt;
    {{ slotProps.prop.foo }}
    &lt;template v-slot:other="otherSlotProps"&gt;
        slotProps is not available here
    &lt;/template&gt;
&lt;/demo&gt;
                            </pre>

                            <p>
                                只要出现多个slot, 始终为所有slot使用完整的template元素语法
                            </p>

                            <p class="bg-info text-info">
                                当然, 命名独占插槽也可以使用类似的缩写语法.
                            </p>
                        </div>
                    </div>

                    <div>
                        <h4>解构插槽Prop</h4>
                        <div>
                            <p>
                                作用域slot的内部工作原理是将slot内容包括在一个传入单个参数的函数里 :
                            </p>
                            <pre>function (slotProps) { /* 插槽内容 */ }</pre>
                            <p>
                                可以理解成, slot通过v-bind绑定好的所有slot prop会被存储为一个对象, 这个对象此时还没有名字, 而我们可以通过带值v-slot指令给这个对象起一个名字,
                                这样就可以通过这个名字访问该slot绑定的所有prop. 本质上这个对象会作为参数传递到上述函数, 然后插槽内容被定义在函数体内.
                            </p>

                            <p>
                                这意味着v-slot的值可以是任何能够作为函数定义中的参数的js表达式.
                            </p>
                            <p>
                                复习一下js 的destruction assignment(解构 / 析构 赋值)语法(es2015) : 一些变量可以通过一个对象来进行赋值, 例如

                            </p>

                            <pre>let a, b; ({a, b} = {a: 1, b: 2})</pre>

                            <p>
                                通过把定义的变量括起来, 然后用一个对象赋值, 可以把对象中对应属性的值赋值给对应的变量.
                            </p>

                            <p>对于函数的参数, 也可以如此, 因为参数也是变量 : </p>

                            <pre>function foo({a, b}) {}</pre>
                            <pre>foo({a: 1, b: 2})</pre>
                            <p>
                                甚至, 我们还可以通过冒号加别名的形式, 给变量重命名 :
                            </p>
                            <pre>let a, b; ({a: c, b: d} = {c: 1, b: 2})</pre>

                            <br>

                            <p>
                                所以我们可以通过这种语法使得代码更加简洁(省去了给slot props起名的步骤), 例如 :
                            </p>
                            <pre>
Vue.component("app46", { 
    props: ['propA', 'propB'], 
    template: `
        &lt;p&gt;
            &lt;slot :slotPropA="propA" 
                     :slotPropB="propB"&gt;
            &lt;/slot&gt;
        &lt;/p&gt;
    ` 
})
                            </pre>

                            <pre>
&lt;app46 id="app-46" 
          :prop-a="{one: 1, two: 2}" 
          :prop-b="{three: 3, four: 4}" 
          v-slot="{slotPropA, slotPropB: B}"&gt;
    {{slotPropA}}, {{B}}
&lt;/app46&gt;
                            </pre>

                            <pre>
let app46 = new Vue({
  el: "#app-46",
});
                            </pre>

                            <div class="code-output">
                                <samp>
                                    <app46 id="app-46"
                                           :prop-a="{one: 1, two: 2}"
                                           :prop-b="{three: 3, four: 4}"
                                           v-slot="{slotPropA, slotPropB: B}">
                                        {{slotPropA}}, {{B}}
                                    </app46>
                                </samp>
                            </div>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="246">动态插槽名</h2>
                    <div>
                        <pre>v-slot:[dynamicSlotName]</pre>
                    </div>
                </article>

                <article>
                    <h2 id="247">具名插槽的缩写</h2>
                    <div>
                        <p>
                            <code>v-slot:</code>缩写对应<code>#</code>
                        </p>
                        <p>
                            然而，和其它指令一样，该缩写只在指令有参数的时候才可用
                        </p>
                        <p>
                            如果仍然希望使用缩写, 可以使用<code>#default</code>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="248">其他</h2>
                    <div>
                        <p>
                            <strong>
                                插槽prop允许我们将插槽转换为可复用的模板, 这些模板基于输入的prop渲染出不同内容.
                            </strong>
                        </p>

                        <p>
                            这在设计封装数据同时允许父级组件自定义部分布局的可复用组件时时最有用的, 例如 :
                        </p>

                        <pre>
Vue.component("todo-list", {
  props: {
    todos: {
      type: Array,
      required: true,
    },
  },
  computed: {
    filteredTodos: function () {
      return this.todos.filter((todo) => !todo.isComplete);
    },
  },
  template: `
        &lt;ul>
            &lt;li v-for="todo in filteredTodos"
                :key="todo.id">
                &lt;slot name="todo" :todo="todo">
                    {{ todo.text }}
                &lt;/slot>
            &lt;/li>
        &lt;/ul>
    `
});
                        </pre>

                        <p>
                            当我们使用该组件时, 可以选择为todo定义一个不一样的<code>&lt;template&gt;</code>作为替代方案, 并且可以从组组件获取数据 :
                        </p>

                        <pre>
&lt;todo-list id="todoList"
              :todos="todos"&gt;
    &lt;template #todo="{todo}"&gt;
        id: {{todo.id}}, context: {{todo.context}}, createdTime: {{todo.createdTime}}
    &lt;/template&gt;
&lt;/todo-list&gt;
                        </pre>

                        <pre>
let todoList = new Vue({
  el: "#todoList",
  data: {
    todos: [
      {
        id: 0,
        context: "learn vue",
        createdTime: "2020/1/1",
        isComplete: false,
      },
      {
        id: 1,
        context: "learn python",
        createdTime: "2020/2/30",
        isComplete: true,
      },
      {
        id: 2,
        context: "front end",
        createdTime: "2020/8/9",
        isComplete: false,
      },
      {
        id: 3,
        context: "back end",
        createdTime: "2020/4/15",
        isComplete: true,
      },
      {
        id: 4,
        context: "spring boot learning",
        createdTime: "2020/3/1",
        isComplete: false,
      },
    ],
  },
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <todo-list id="todoList"
                                           :todos="todos">
                                    <template #todo="{todo}">
                                        id: {{todo.id}}, context: {{todo.context}}, createdTime: {{todo.createdTime}}
                                    </template>
                                </todo-list>
                            </samp>
                        </div>

                        <p>
                            更多现实生活中的作用域插槽用法, 推荐了解
                            <a href="https://github.com/Akryum/vue-virtual-scroller">Vue Virtual Scroller</a>,
                            <a href="https://github.com/posva/vue-promised">Vue Promised</a>,
                            <a href="https://github.com/LinusBorg/portal-vue">Portal Vue</a>
                            等库
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="25">动态组件&异步组件</h1>

                <article>
                    <h2 id="251">在动态组件上使用keep-alive</h2>
                    <div>
                        <p>
                            在一个标签上用<code>is</code> attribute来切换不同的组件时, 可能希望切换时保存这些组件的状态, 以避免重复渲染导致性能问题.
                            例如定义以下两个组件 :
                        </p>

                        <pre>
Vue.component("dynamicComponentA", {
    template: `
        &lt;div&gt;
            &lt;ul class="nav nav-pills"&gt;
                &lt;li&gt;&lt;a href="#dynamicTab0" data-toggle="tab"&lt;First&lt;/agt;&lt;/li&gt;
                &lt;li&gt;&lt;a href="#dynamicTab1" data-toggle="tab"&gt;Second&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href="#dynamicTab2" data-toggle="tab"&gt;Third&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
             &lt;div&gt; class="tab-content"&gt;
                &lt;p class="tab-pane" id="dynamicTab0"&gt;
                    content of the first tab 
                &lt;/p&gt;
                &lt;p class="tab-pane" id="dynamicTab1"&gt;
                    content of the second tab
                &lt;/p&gt;
                &lt;p&gt; class="tab-pane" id="dynamicTab2"&gt;
                    content of the third tab
                &lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        `
})
                        </pre>
                        <pre>
Vue.component("dynamicComponentB", {
    template: `
        &lt;p&gt;Dynamic component b&lt;/p&gt;
    `
})                            
                        </pre>

                        <br>

                        <p>
                            然后再通过<code>is</code> attribute对两个组件进行切换 :
                        </p>

                        <pre>
let app47 = new Vue({
    el: "#app-47",
    data: {
        currentTabComponent: ''
    },
    template: `
        &lt;div&gt;
            &lt;div class="btn-group"&gt;
                &lt;button class="btn btn-default"
                        @click="currentTabComponent = 'dynamicComponentA'"&gt;
                    Toggle a
                &lt;/button&gt;
                            
                &lt;button class="btn btn-default"
                        @click="currentTabComponent = 'dynamicComponentB'"&gt;
                    Toggle b
                &lt;/button&gt;
            &lt;/div&gt;
            &lt;component :is="currentTabComponent"&gt;&lt;/component&gt;
        &lt;/div&gt;
            `
})
                        </pre>

                        <pre>&lt;div id="app-47"&gt;&lt;/div&gt;</pre>

                        <div class="container code-output">
                            <samp>
                                <div id="app-47"></div>
                            </samp>
                        </div>

                        <p>
                            可以看到, 当我们从toggle b切换到toggle a时, 之前toggle a 内打开的tab会被关闭, 因为切换<code>is</code>
                            attribute值时前一个组件将失活, 新的组件被重新创建.
                        </p>

                        <p>
                            如果我们想要保留动态切换的组件的活性, 可以简单地使用元素<code>keep-alive</code>将包含<code>is</code> attribute的元素包裹住,
                            注意必须将切换组件作为<code>keep-alive</code>的直接子元素
                        </p>
                        <pre>
&lt;keep-alive&gt;
    &lt;component :is="currentTabComponent"&gt;&lt;/component&gt;
&lt;/keep-alive&gt;                            
                        </pre>
                        <div class="container code-output">
                            <samp>
                                <div id="app-48"></div>
                            </samp>
                        </div>

                        <p>
                            <code>keep-alive</code>属于Vue的内置组件 (目前已经学了3个内置组件, keep-alive, component, slot).
                            它会缓存不活动的组件实例, 自己不会被渲染为一个DOM元素, 也不会出现在组件链中.
                        </p>

                        <p>
                            组件在<code>&lt;keep-alive&gt;</code>内切换时, 组件的 <code>activated</code> 和
                            <code>deactivated</code>两个生命周期钩子会被对应执行. 此外, 还要求同时只有一个直接子组件被渲染, 意味着<code>v-for</code>在
                            <code>keep-alive</code>中不工作
                        </p>

                        <div class="alert-info">
                            2.2.0及更高版本中, <code>activated</code> 和
                            <code>deactivated</code>会在<code>&lt;keep-alive&gt;</code>树内的所有嵌套组件中触发
                        </div>

                        <br>

                        <p>
                            <code>keep-alive</code>还有一些可用的props :
                        </p>
                        <ul>
                            <li>
                                include: 类型为字符串或正则表达式或数组. 只有名称匹配的组件才会被缓存. <br>
                                值为字符串时, 可用逗号分隔多个匹配的组件名 <br>
                                值为正则表达式时需要用<code>v-bind</code>指定正则表达式 <br>
                                值为数组时( 也要用v-bind绑定静态值 ), 项只能为字符串类型且字符串不能通过逗号同时指定多个匹配组件名
                            </li>
                            <li>exclude: 只有名称不匹配的组件才会被缓存</li>
                            <li>max: number类型, 指出最多可以缓存多少组件实例, 超过时销毁最久未使用的组件实例</li>
                        </ul>
                </article>

                <article>
                    <h2 id="252">异步组件</h2>
                    <div>
                        <p>
                            在大型应用中, 我们需要将应用分割成小的代码块, 且只在需要时才从服务器加载一个模块. 为了简化, Vue允许我们以一个工厂函数的方式定义你的组件, 这个工厂函数会异步解析组件定义.
                            Vue只有在这个组件需要被重新渲染的时候才会触发该工厂函数, 且会把结果缓存供未来重新渲染, 例如 :
                        </p>

                        <pre>
Vue.component('async-component', function(resolve, reject) {
    setTimeOut(function () {
        resolve({
            template: '&lt;div&gt;async component&lt;/div&gt;'
        }), 2000
    })
})
                        </pre>

                        <p>
                            工厂函数收到一个 <code>resolve</code> 回调, 这个回调函数会在从服务器得到组件定义时被调用. 也可以用 <code>reject(reason)</code>
                            来表示加载失败. 如何获取组件取决于我们. 推荐做法是将异步组件和webpack 的 code-splitting功能一起使用 :
                        </p>

                        <pre>
Vue.component('async-webpack-component', function(resolve) {
    // require 语法告诉webpack自动将构建代码分割成多个包, 
    // 这些包通过ajax请求加载
    require(['./my-async-component'], resolve)
})                            
                        </pre>

                        <p>
                            也可以在工厂函数返回一个<code>Promise</code>, 所以可以把webpack2和es2015语法结合, 例如 :
                        </p>

                        <pre>
Vue.component('async-webpack-component', () => import('./my-asnyc-component'))                            
                        </pre>

                        <p>
                            使用局部注册时也可以提供一个返回<code>Promise</code>的函数 :
                        </p>

                        <pre>
new Vue({
    component: {
        'my-component': () => import('./my-async-component')
    }
})
                        </pre>
                    </div>

                    <div>
                        <h4>处理加载状态</h4>
                        <p>
                            异步工厂函数还可以返回如下格式的对象 :
                        </p>
                        <pre>
const AsyncComponent = () => ({
    //需要加载的组件 (应为一个Promise对象)
    component: import('./MyComponent.vue'),

    //异步加载时使用的组件
    loading: LoadingComponent,

    //加载失败时使用的组件
    error: ErrorComponent,

    //展示加载时组件的延时时间, 默认200mx
    delay: 200,

    //如果提供超时时间且组件加载超时, 则使用加载失败时使用的组件, 默认值为Infinity
    timeout: 3000
})                            
                        </pre>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="26">处理边界情况</h1>

                <article>
                    <h2 id="261">访问元素&组件</h2>

                    <div>
                        <h4>访问根实例</h4>
                        <p>
                            每个<code>new Vue</code>实例的子组件中, 其根实例可以通过<code>$root</code>属性访问, 例如, 在此根实例中 :
                        </p>

                        <pre>
let app49 = new Vue({
    el: "#app-49",
    data: {
        foo: 1
    }，
    computed：{
        bar: function() {
            return foo * 2
        }
    },
    methods: {
        baz: function() {
            return bar * 2
        }
    }
})                            
                        </pre>

                        <p>
                            所有子组件可以将该实例作为一个全局store来访问或使用
                        </p>

                        <pre>
Vue.component('app49-child', {
    template: `&lt;p&gt; foo: {{$root.foo}}, bar: {{$root.bar}}, baz: {{$root.baz()}}&lt;/p&gt;`
})                            
                        </pre>

                        <pre>
&lt;div id="app-49"&gt;
    &lt;app49-child&gt;&lt;/app49-child&gt;
&lt;/div&gt;                            
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-49">
                                    <app49-child></app49-child>
                                </div>
                            </samp>
                        </div>

                    </div>

                    <div>
                        <h4>访问父级组件实例</h4>
                        <p>
                            和<code>$root</code>类似, <code>$parent</code> property可以用来从一个子组件访问父组件的实例.
                        </p>

                        <p>
                            我们还可以在创建组件时使用<code>parent</code>选项, 它属于选项/组合, 类型为<code>Vue instance</code>，
                            用于指向已经创建的实例的父实例, 在两者间建立父子关系. 子实例可以通过<code>$parent</code>访问父实例,
                            子实例被推入父实例的 <code>$children</code>数组中.
                        </p>

                        <div class="label-warning text-warning">
                            绝大多数情况下, 触及父组件会使应用更难调试和理解.
                        </div>
                    </div>

                    <div>
                        <h4>访问子组件实例或子元素</h4>
                        <p>
                            尽管已经有了prop和事件机制, 有时仍可能需要在js里直接访问一个子组件. 为达到这一目的, 可以通过<code>ref</code>
                            这个attribute为模板内的元素和子组件注册引用信息. 引用信息会注册到父组件的 <code>$refs</code>对象上. 如果用在普通的DOM元素上, 引用指向DOM元素,
                            若用在子组件上, 引用就指向组件实例 :
                        </p>

                        <pre>
&lt;!-- vm.$refs.p 为DOM节点 --&gt;
&lt;p ref="p"&gt;hello&lt;/p&gt;

&lt;!-- vm.$refs.child 为子组件实例 --&gt;
&lt;child-component ref="child"&gt;&lt;/child-component&gt;
                        </pre>

                        <p>
                            当 <code>ref</code> 和 <code>v-for</code>一起使用时, 引用信息为包含DOM节点或组件实例的数组 :
                        </p>

                        <pre>
Vue.component('app50-child', {
    template: `&lt;li&gt;app50-child&lt;/li&gt;`
})
                        </pre>

                        <pre>
&lt;ul id="app-50"&gt;
    &lt;strong&gt;ref list-item: {{listItemRef}}&lt;/strong&gt;
    &lt;li ref="list-item" v-for="item in items"&gt;
        {{item}}
    &lt;/li&gt;
    &lt;strong&gt;ref childComponent : {{childComponentRef}}&lt;/strong&gt;
    &lt;app50-child ref="childComponent" v-for="index in 4" :key="index"&gt;&lt;/app50-child&gt;
&lt;/ul&gt;
                        </pre>

                        <pre>
let app50 = new Vue({
    el: "#app-50",
    data: {
        items: [ 'item0', 'item1', 'item2', 'item3' ],
        listItemRef: '',
        childComponentRef: ''
    },
    mounted: function () {
        this.listItemRef = this.$refs['list-item'].toString();
        this.childComponentRef = this.$refs.childComponent.toString();
    }
})                            
                        </pre>

                        <div class="code-output">
                            <samp>
                                <ul id="app-50">
                                    <strong>ref list-item : {{listItemRef}}</strong>
                                    <li ref="list-item"
                                        v-for="item in items">
                                        {{item}}
                                    </li>
                                    <strong>ref childComponent : {{childComponentRef}}</strong>
                                    <app50-child ref="childComponent"
                                                 v-for="index in 4"
                                                 :key="index"></app50-child>
                                </ul>
                            </samp>
                        </div>
                        <!-- this.$refs.['list-item'] = 
                        this.$refs.childComponent =  -->

                        <div class="bg-danger danger-text">
                            因为ref本身是作为渲染结果被创建的, 在初始渲染时不能访问它们 —— 它们不存在. <code>$refs</code>也不是响应式的, 因此不应该试图用它在模板中做插值 /
                            数据绑定. 所以应该避免在<code>template</code>和<code>computed</code>中使用它.
                        </div>

                        <br>

                        <p>
                            Vue还提供了一种访问直接子组件的方式 —— <code>vm.$children</code>,
                            类型为<code>Array&lt;Vue instance&gt;</code>. 注意<code>$children</code>并不保证顺序,
                            也不是响应式的. 如果发现自己正在使用它进行数据绑定, 考虑使用一个数组配合<code>v-for</code>来生成子组件,
                            并使用Array作为真正的来源.
                        </p>
                    </div>

                    <div>
                        <h4>依赖注入</h4>
                        <p>
                            我们可以通过 <code>provide</code>和<code>inject</code>这两个option来完成依赖注入.
                        </p>

                        <p>
                            <code>provide</code>为一个对象或返回对象的函数, 该对象包含可注入其子孙的属性. 在该对象中, 可以使用 es2015 Symbols作为key, 但只在
                            支持<code>Symbol</code>和<code>Reflect.ownKeys</code>的环境下可工作.
                        </p>

                        <p>
                            在任何后代组件里, 我们都可以使用<code>inject</code>选项来接受指定的想要添加到这个实例上的property.
                            <code>inject</code>选项应该是 :
                        </p>
                        <ul>
                            <li>
                                一个字符串数组, 或
                            </li>
                            <li>
                                一个对象, 对象的key是本地的绑定名, value是 :
                                <ul>
                                    <li>在可用的注入内容中搜索用的key (字符串或Symbol), 或</li>
                                    <li>
                                        一个对象, 该对象的 <code>from</code>属性是在可用注入内容中搜索用的key;
                                        <code>default</code> 属性是在降级情况下使用的value (与prop一样, 需要通过工厂函数返回对象或数组值)
                                    </li>
                                </ul>
                            </li>
                        </ul>

                        <p>
                            实际上, 可以把依赖注入看作一部分"大范围有效的prop", 除了
                            祖先组件不需要知道哪些后代组件使用它提供的属性, 后代组件也不需要知道被注入的属性来自哪里.
                        </p>

                        <div class="alert-info">
                            <code>provide</code>和<code>inject</code>绑定不是响应式的. 这是可以为之的.
                            然而如果传入了一个可监听的对象,
                            那么其对象的属性还是可响应的.
                        </div>

                        <br>

                        <div class="alert-warning">
                            <code>provide</code>和<code>inject</code>主要在开发高阶插件/组件库时使用. 并不推荐用于普通应用程序代码中.
                        </div>

                    </div>
                </article>

                <article>
                    <h2 id="262">程序化的事件侦听器</h2>
                    <p>
                        我们已经知道了<code>$emit</code>的用法, 它可以被<code>v-on</code>侦听, 但是Vue实例同时在其事件接口中提供了其他方法 :
                    </p>
                    <ul>
                        <li>通过<code>$on(eventName, eventHandler)</code>侦听一个事件</li>
                        <li>通过<code>$once(eventName, eventHandler)</code>一次性侦听一个事件</li>
                        <li>通过<code>$off(eventName, eventHandler)</code>停止侦听一个事件</li>
                    </ul>

                    <p>通常这些都用不上=-=</p>

                    <div class="text-info bg-info">
                        注意Vue事件系统不同于浏览器的EventTarget API. <code>$emit</code>, <code>$on</code>, <code>$off</code> 不是
                        <code>dispatchEvent</code>, <code>addEventListener</code>, <code>removeEventListener</code>的别名
                    </div>
                </article>

                <article>
                    <h2 id="263">循环引用</h2>

                    <div>
                        <h4>递归组件</h4>
                        <p>
                            组件可以在其模板中调用自身, 但是只能通过<code>name</code>选项. 当我们使用<code>Vue.component</code>全局注册一个组件时,
                            其第一个参数(全局id)会被自动设置为该组件的<code>name</code>选项. 需要注意递归是条件性的 (例如使用一个最终为false的v-if)
                        </p>
                    </div>

                    <div>
                        <h4>组件间循环引用</h4>
                        <p>
                            如果一个组件A依赖于组件B而组件B又依赖于A, 则会产生一个悖论. 当通过<code>Vue.component</code>全局注册组件时, 这个悖论会被自动解开.
                            但是如果使用<em>模块系统</em>依赖/导入组件, 例如通过webpack或browserify, 则遇到错误. 这种情况下, 需要给模块系统一个点,
                            在那里 "A <em>反正</em>是需要B的, 但我们不需要先解析B "(A needs B <em>eventually</em>, but there's no need to
                            resolve B first ).
                            例如, 将该点设置在A上, 我们知道产生循环引用的组件是B,
                            所以我们会等到生命周期钩子 <code>beforeCreate</code>时去注册它 :
                        </p>

                        <pre>
beforeCreate: function () {
    this.$options.components.B = required('./B.vue').default
}
                        </pre>

                        <p>
                            或者, 在本地注册组件时, 可以使用webpack的异步<code>import</code> :
                        </p>

                        <pre>
components: {
    B: () => import("./B.vue")
}
                        </pre>

                        <p>
                            这样问题就解决了.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="264">模板定义的替代品</h2>

                    <div>
                        <h4>内联模板</h4>
                        <p>
                            使用<code>inline-template</code> attribute到一个子组件上, 这个组件将用它的内容content作为该组件的模板,
                            而不是将其作为被分发的内容.
                        </p>
                        <div class="waring-text bg-warning">
                            不过, 该attribute会让模板作用域难以理解,
                            因此在组件内优先使用<code>template</code>选项或<code>.vue</code>文件里的一个<code>&lt;template&gt;</code>元素定义模板
                        </div>

                        <pre>
Vue.component("inline-tmp-comp", {
    data: function () {
        return {
            a: 1, 
            b: 2
        }
    }
})                            
                        </pre>

                        <pre>
&lt;inline-tmp-comp inline-template id="inline-tmp-demo"&gt;
    &lt;p&gt;This content will be add as the template of inline-tmp-comp, 
    rather than the template of root instance.
    Thus, we should also follow the "single root element rule"&lt;/p&gt;
    &lt;p&gt;a: {{a}}, b: {{b}}&lt;/p&gt;
&lt;/inline-tmp-comp&gt;
                        </pre>

                        <pre>
let inlineTmpDemo = new Vue({
    el: "#inline-tmp-demo"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <inline-tmp-comp inline-template
                                                 id="inline-tmp-demo">
                                    <div>
                                        <p>These content will be add as the template of inline-tmp-comp, rather than the
                                            template of root instance. Thus, we should also follow the "single root
                                            element
                                            rule"
                                        </p>
                                        <p>a: {{a}}, b: {{b}}</p>
                                    </div>
                                </inline-tmp-comp>
                            </samp>
                        </div>

                        <p>
                            如果去掉该attribute, Vue就会报错, 提示不能挂载该组件因为我们没有给inline-tmp-comp组件定义模板或render function
                        </p>

                        <p>
                            注意内联模板需要定义在Vue所属的DOM元素内.
                        </p>

                    </div>

                    <div>
                        <h4>X-Template</h4>
                        <p>
                            另一个定义模板的方式是在一个<code>script</code>元素上, 加上<code>type="text/x-template"</code>, 再用一个id将模板引用过去,
                            例如 :
                        </p>
                        <pre>
&lt;script type="text/x-template" id="hello-world-template"&gt;
    &lt;p&gt;Hello world&lt;/p&gt;
&lt;/script&gt;                            
                        </pre>
                        <pre>
Vue.component('hello-world', {
    template: "#hello-world-template"
})                            
                        </pre>

                        <pre>
&lt;hello-world id="hello-world-app"&gt;&lt;/hello-world-app&gt;
                        </pre>

                        <pre>
let helloWorldApp = new Vue({
  el: "#hello-world-app",
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <script type="text/x-template"
                                        id="hello-world-template">
                                    <p>Hello world</p>
                                </script>

                                <hello-world id="hello-world-app"></hello-world>
                            </samp>
                        </div>

                        <p>注意 x-template需要定义在Vue所属的DOM元素之外</p>

                        <div class="bg-danger text-danger">
                            这些可以用在模板特别大的demo或极小型应用, 其他情况避免使用, 因为这会将模板和组件定义分离开.
                        </div>

                    </div>
                </article>

                <article>
                    <h2 id="265">控制更新</h2>

                    <div>
                        <h4>强制更新</h4>
                        <p>
                            <code>vm.$forceUpdata()</code>
                        </p>
                        <div class="warning text alert-warning">
                            如果发现需要在Vue中做强制更新, 基本是做错了某些事(比如直接通过数组下标修改项...)
                        </div>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="31">进入/离开&列表过渡</h1>

                <article>
                    <h2 id="311">概述</h2>
                    <div>
                        <p>
                            Vue在插入, 更新, 或移除DOM时, 提供了多种不同方式的应用过渡效果. 包括 :
                        </p>
                        <ul>
                            <li>在CSS过渡和动画中自动应用class</li>
                            <li>可以配合使用第三方CSS动画库, 如Animate.css</li>
                            <li>在过渡钩子函数中使用js直接操作DOM</li>
                            <li>配合第三方js动画库</li>
                        </ul>
                    </div>
                </article>

                <article>
                    <h2 id="312">单元素/组件的过渡</h2>

                    <div>
                        <p>
                            Vue提供了<code>transition</code>的封装组件, 在下列情况中, 可以给任何元素和组件添加进入/离开过渡
                        </p>
                        <ul>
                            <li>条件渲染(v-if)</li>
                            <li>条件展示(使用v-show)</li>
                            <li>动态组件( 通过<code>is</code>切换 )</li>
                            <li>组件根节点</li>
                        </ul>

                        <pre>
&lt;div id="app-51"&gt;
    &lt;button @click="show !=show"&gt;Toggle&lt;/button&gt;
    &lt;transition name="fade"&gt;
        &lt;p v-if="show"&gt;Hello Vue&lt;/p&gt;
    &lt;/transition&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app51 = new Vue({
    el: "#app-51",
    data: {
        show: true
    }
})
                        </pre>

                        <pre>
.fade-enter-active, .fade-leave-active {
    transition: opacity .5s;
}

.fade-enter, .fade-leave-to {
    opacity: 0;
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-51">
                                    <button @click="show = !show">Toggle</button>
                                    <transition name="fade">
                                        <p v-if="show">Hello Vue</p>
                                    </transition>
                                </div>
                            </samp>
                        </div>

                        <p>
                            当插入或删除包含在<code>transition</code>组件中的元素时, Vue将会做以下处理 :
                        </p>

                        <ol>
                            <li>自动检测目标元素是否应用了css过渡transition或动画animation, 若是, 则在恰当的实际添加/删除css类名</li>
                            <li>如果过渡组件提供了js钩子函数, 这些钩子函数将在恰当的时机被调用</li>
                            <li>
                                如果没有找到js钩子并且没有检测到css transition/animation, DOM操作(插入/删除)在下一帧中立刻执行
                                (这里指的是浏览器逐帧动画机制, 和Vue的 <code>nextTick</code> 概念不同)
                            </li>
                        </ol>
                    </div>

                    <div>
                        <h4>过渡的类名</h4>

                        <p>
                            在进入/离开的过渡中, 会有6个class切换.
                        </p>

                        <ol>
                            <li class="li">
                                <code>v-enter</code> : 定义进入过渡的开始状态. 在元素被插入之前生效, 在元素被插入后的下一帧移除
                            </li>
                            <li class="li">
                                <code>v-enter-active</code> : 定义进入过渡生效时的状态. 在整个进入过渡的阶段中应用, 在元素被插入之前生效, 在过渡/动画完成后移除.
                                这个类可以被用来定义进入过渡的过程时间, 延迟和曲线函数
                            </li>
                            <li class="li">
                                <code>v-enter-to</code> : 2.1.8以上定义进入过渡的结束状态. 在元素被插入后的下一帧生效 (与此同时
                                <code>v-enter</code>被移除), 在过渡/动画完成后移除
                            </li>
                            <li class="li">
                                <code>v-leave</code> : 定义离开过渡的开始状态. 在离开过渡被触发时立刻生效, 下一帧被移除.
                            </li>
                            <li class="li">
                                <code>v-leave-active</code> : 定义离开过渡生效时的状态. 在整个离开过渡的阶段中应用, 在离开过渡被触发时立刻生效, 在过渡/动画完成后移除.
                                这个类可以被用来定义离开过渡的过程时间, 延迟和曲线函数.
                            </li>
                            <li class="li">
                                <code>v-leave-to</code> : 2.1.8版本以上定义离开过渡的结束状态. 在离开过渡被触发之后下一帧生效(于此同时
                                <code>v-leave</code> 被删除), 在过渡/动画完成之后移除.
                            </li>
                        </ol>

                        <p>
                            对于这些过渡中切换的类名, 使用一个没有定义 <code>name</code> attribute的<code>&lt;transition&gt;</code>,
                            <code>v-</code>为这些类名的默认前缀, 如果使用了<code>name</code> attribute,
                            则前缀会被替换为<code>name</code>的值后跟一个<code>-</code>
                        </p>
                    </div>

                    <div>
                        <h4>CSS过渡</h4>
                        <p>
                            例子:
                        </p>

                        <pre>
&lt;div id="app-52"&gt;
    &lt;button @click="show = !show"&gt;
        Toggle render
    &lt;/button&gt;
    &lt;transition name="slide-fade"&gt;
        &lt;p v-if="show"&gt;Hello&lt;/p&gt;
    &lt;/transition&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app52 = new Vue({
    el: "#app-52",
    data: {
        show: true
    }
});
                        </pre>

                        <pre>
.slide-fade-enter-active {
    transition: all .3s ease;
}

.slide-fade-leave-active {
    transition: all .8s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter, .slide-fade-leave-to {
    transform: translateX(10)px;
    opacity: 0;
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-52">
                                    <button @click="show = !show">
                                        Toggle render
                                    </button>
                                    <transition name="slide-fade">
                                        <p v-if="show">Hello</p>
                                    </transition>
                                </div>
                            </samp>
                        </div>
                    </div>

                    <div>
                        <h4>CSS动画</h4>
                        <p>
                            用法与CSS过渡相同, 区别在于在动画中<code>v-enter</code>类名在节点插入DOM后不会立刻删除,
                            而是在<code>animationed</code>事件触发时删除.
                        </p>
                        <p>
                            示例 :
                        </p>

                        <pre>
&lt;div id="app-53"&gt;
    &lt;button @click="show = !show"&gt;
        Toggle render
    &lt;/button&gt;
    &lt;transition name="bounce"&gt;
        &lt;div v-if="show" style="width: 50px; 
                                   height: 50px; 
                                   background-color: red"&gt;
        &lt;/div&gt;
    &lt;/transition&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app53 = new Vue({
    el: "#app-53",
    data: {
        show: true
    }
});
                        </pre>

                        <pre>
.bounce-enter-active {
    animation: bounce-in .5s
}

.bounce-leave-active {
    animation: bounce-in .5s reverse
}

@keyframes bounce-in {
    0% {
        transform: scale(0)
    }

    50% {
        transform: scale(1.5)
    }

    100% {
        transform: scale(1)
    }
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-53">
                                    <button @click="show = !show">
                                        Toggle render
                                    </button>
                                    <transition name="bounce">
                                        <div v-if="show"
                                             style="width: 50px; 
                                                                   height: 50px; 
                                                                   background-color: red">
                                        </div>
                                    </transition>
                                </div>
                            </samp>
                        </div>
                    </div>

                    <div>
                        <h4>自定义过渡类名</h4>
                        <p>
                            我们可以通过该组件的以下prop来自定义过渡类名 :
                        </p>
                        <ul>
                            <li>enter-class</li>
                            <li>enter-active-class</li>
                            <li>enter-to-class</li>
                            <li>leave-class</li>
                            <li>leave-active-class</li>
                            <li>leave-to-class</li>
                        </ul>

                        <p>
                            他们的优先级高于普通的类名, 这对Vue的过渡系统和其他第三方CSS动画库结合使用非常有用
                        </p>
                    </div>

                    <div>
                        <h4>同时使用过渡和动画</h4>
                        <p>
                            Vue为了知道过渡的完成, 必须设置相应的事件监听器. 它可以是<code>transitioned</code>或<code>animationed</code>,
                            这取决于给元素应用的css规则.如果使用了其中任何一种, Vue能自动识别类型并设置监听
                        </p>

                        <p>
                            但在某些场景需要给同一个元素同时设置两种. 比如<code>animation</code>很快被触发并完成而<code>transition</code>效果还没结束.
                            这时需要用<code>type</code>
                            attribute并设置为<code>animation</code>或<code>transition</code>来明确声明需要Vue监听的类型
                        </p>
                    </div>

                    <div>
                        <h4>显性的过渡持续时间</h4>
                        <p>
                            在很多情况下, Vue可以自动得出过渡效果的完成实际. 默认情况下,
                            Vue会等待其在过渡效果的根元素的第一个<code>transition</code>或<code>animationed</code>事件.
                            然而我们可以自定义该时机 —— 比如, 其中一些嵌套的内部元素相比过渡效果的根元素有延迟的或更长的过渡效果.
                        </p>
                        <p>
                            这种情况下, 可以用<code>&lt;transition&gt;</code>组件上的<code>duration</code>
                            attribute定制一个显性的过渡持续时间(ms), 例如 :
                        </p>

                        <pre>
&lt;transition :duration="1000"&gt;...&lt;/transition&gt;
                        </pre>

                        <p>
                            也可以自定义进入和离开的持续时间, 绑定一个静态对象, 给出enter 和 leave properties, 例如 :
                        </p>
                        <pre>
&lt;transition :duration="{enter: 500, leave: 800}"&gt;...&lt;/transition&gt;
                        </pre>
                    </div>

                    <div>
                        <h4>js hook</h4>
                        <p>
                            可以在属性中声明js hook, 例如:
                        </p>
                        <pre>
&lt;transition
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
    @enter-cancelled="enterCancelled"

    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
    @leave-cancelled="leaveCancelled"
&gt;
    &lt;!----&gt;
&lt;/transition&gt;
                        </pre>

                        <p>
                            这些钩子函数都以过渡元素的DOM为第一个参数, 在与CSS结合使用时第二个参数<code>done</code>是一个可选的回调函数
                        </p>

                        <div class="bg-warning text-warning">
                            当只用js过渡时, 在<code>enter</code>和<code>leave</code>中必须使用<code>done</code>进行回调
                        </div>

                        <br>

                        <div class="bg-info text-info">
                            推荐对于仅使用js过渡的元素添加<code>:css="false"</code>, Vue会跳过CSS的检测. 这也可以避免过渡过程受CSS影响
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="313">初始渲染的过渡</h2>
                    <div>

                        <p>
                            <code>appear</code> prop(类型为boolean)可以设置在<code>transition</code>组件上来设置节点在初始渲染的过渡
                        </p>
                        <pre>
&lt;transition appear&gt;
&lt;!----&gt;
&lt;/transition&gt;
                    </pre>

                        <p>
                            同样, 我们可以通过下面几个attributes自定义css类名
                        </p>

                        <ul>
                            <li>appear-class</li>
                            <li>appear-to-class</li>
                            <li>appear-active-class</li>
                        </ul>

                        <p>
                            同时还有以下自定义js钩子
                        </p>

                        <ul>
                            <li>@before-appear</li>
                            <li>@appear</li>
                            <li>@after-appear</li>
                            <li>@appear-cancelled</li>
                        </ul>
                    </div>
                </article>

                <article>
                    <h2 id="314">多个元素的过渡</h2>

                    <div>
                        <p>
                            可以使用 <code>v-if</code>, <code>v-else-if</code>, <code>v-else</code> 实现多个元素之间的过渡.
                            最常见的多元素过渡为一个列表和描述列表为空消息的元素 :
                        </p>

                        <pre>
&lt;div id="app-54"&gt;
    &lt;button @click="add"&gt;add&lt;/button&gt;
    &lt;button @click="del"&gt;add&lt;/button&gt;
    &lt;transition&gt;
        &lt;ul v-if="items.length > 0"&gt;
            &lt;li v-for="item in items"&gt;{{item}}&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p v-else&gt;No items left&lt;/p&gt;
    &lt;/transition&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let app54 = new Vue({
    el: "#app-54",
    data: {
        items: [1, 1, 4, 5, 1, 4]
    },
    methods: {
        add: function () {
            this.items.push( Math.round(Math.random() * 100) )
        },

        del: function () {
            this.items.pop();
        }
    }
});
                        </pre>

                        <pre>
.v-enter-active, .v-leave-active {
    transition: all .5s ease;
}

.v-enter, .v-leave-to {
    opacity: 0;
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-54">
                                    <button @click="add">add</button>
                                    <button @click="del">delete</button>
                                    <transition>
                                        <ul v-if="items.length > 0">
                                            <li v-for="item in items">{{item}}</li>
                                        </ul>
                                        <p v-else>No items left</p>
                                    </transition>
                                </div>
                            </samp>
                        </div>

                        <div class="bg-warning text-warning">
                            注意当有<strong>相同tag</strong>的元素切换时, 需要通过设置元素的唯一<code>key</code> attribute来让Vue区分它们,
                            否则Vue为了效率只会替换同标签元素的content. 即使没有必要, 最好给<code>transition</code>组件中元素都加上<code>key</code>
                            attribute.
                        </div>

                        <br>

                        <p>
                            某些场景下, 可以用<code>key</code> attribute来设置同一个元素的不同状态以代替<code>v-if</code>和<code>v-else</code>,
                            例如 :
                        </p>

                        <pre>
&lt;transition&gt;
    &lt;button :key="isEditing"&gt;
        {{isEditing ? 'Save' : 'Edit'}}
    &lt;/button&gt;
&lt;/transition&gt;
                        </pre>

                        <p>
                            在使用多个<code>v-if</code>的情况下, 也可以通过<code>key</code> attribute达到同样目的,
                            例如 :
                        </p>

                        <pre>
&lt;transition&gt;
    &lt;button :key="docState"&gt;
        {{buttonMessage}}
    &lt;/button&gt;
&lt;/transition&gt;
                        </pre>

                        <p>
                            将buttonMessage定义为一个根据docState进行切换的计算属性, 就可以达到<code>v-if</code>同样的效果 :
                        </p>

                        <pre>
computed: {
    buttonMessage: function() {
        switch (this.docState) {
            case 'saved': return 'Edit'
            case 'edited': return 'Save'
            case 'editing': return 'Cancel'
        }
    }
}
                        </pre>
                    </div>

                    <div>
                        <h4>过渡模式</h4>
                        <p>
                            直接对多个元素进行过渡有个问题, 即一个元素离开过渡的时候另一个元素开始进入过渡. 这是<code>transition</code>组件的默认行为 —— 进入和离开同时发生
                        </p>

                        <p>
                            同时生效的进入和离开过渡不能满足所有要求, Vue为 <code>transition</code>组件 提供了过渡模式 <code>mode</code> attribute,
                            其值为以下:
                        </p>
                        <ul>
                            <li>in-out : 新元素先进行进入过渡, 完成后旧元素才开始离开过渡</li>
                            <li>out-in : 旧元素先进行离开过渡, 完成后新元素才开始进入过渡</li>
                        </ul>
                    </div>
                </article>

                <article>
                    <h2 id="315">多个组件的过渡</h2>

                    <div>
                        <p>
                            多组件的过渡简单很多, 我们不需要使用<code>key</code> attribute. 相反, 我们需要使用<strong>动态组件</strong>
                        </p>

                        <pre>
Vue.component('trans-comp-a', {
    template: `
        &lt;p&gt;transition component a&lt;/p&gt;
    `
})

Vue.component('trans-comp-b', {
    template: `
        &lt;p&gt;transition component b&lt;/p&gt;
    `
})

let app55 = new Vue({
    el: "#app-55",
    data: {
        currentComp: ''
    }
})    
                        </pre>

                        <pre>
&lt;div id="app-55"&gt;
    &lt;input type="radio"
              v-model="currentComp"
              value="trans-comp-a"&gt;

    &lt;input type="radio"
              v-model="currentComp"
              value="trans-comp-b"&gt;

    &lt;transition name="component-fade"
                   mode="out-in"&gt;
        &lt;component :is="currentComp"&gt;&lt;/component&gt;
    &lt;/transition&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
.component-fade-enter-active, .component-fade-leave-active {
    transition: all .5s ease
}

.component-fade-enter, .component-fade-leave-to {
    opacity: 0;
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-55">
                                    <input type="radio"
                                           v-model="currentComp"
                                           value="trans-comp-a">
                                    <input type="radio"
                                           v-model="currentComp"
                                           value="trans-comp-b">
                                    <transition mode="out-in"
                                                name="component-fade">
                                        <component :is="currentComp"></component>
                                    </transition>
                                </div>
                            </samp>
                        </div>

                    </div>

                </article>

                <article>
                    <h2 id="316">列表过渡</h2>

                    <div>
                        <p>
                            目前为止, 对于过渡我们已经了解了 :
                        </p>

                        <ul>
                            <li>单元素/组件的过渡</li>
                            <li>渲染多个元素/组件中的其中一个</li>
                        </ul>

                        <p>
                            那么如何同时渲染整个列表, 比如<code>v-for</code>? 这种场景下, 使用<code>&lt;transition-group</code>&gt;组件.
                            这个组件有以下特点 :
                        </p>

                        <ul>
                            <li>不同于<code>transition</code>组件和<code>keep-alive</code>(只渲染出它的子组件, 被称作抽象组件，本身不会被渲染为DOM元素,
                                也不出现在组件链中),
                                它以一个真实元素呈现: 默认为一个span元素, 也可以通过 tag
                                attribute更换为其他元素</li>
                            <li>过渡mode不可用, 因为不在相互切换特有的元素</li>
                            <li>内部元素总是需要提供唯一的key attribute值</li>
                            <li>CSS过渡类将应用到内部元素, 而不是这个组/容器本身</li>
                        </ul>

                        <p>
                            <code>transition-group</code>将会是我们学习到的最后一个Vue内置组件, 内置组件分别为 :
                        </p>
                        <ul>
                            <li>keep-alive</li>
                            <li>transition</li>
                            <li>transition-group</li>
                            <li>component</li>
                            <li>slot</li>
                        </ul>

                    </div>

                    <div>
                        <h4>列表的进入/离开过渡</h4>

                        <pre>
let app56 = new Vue({
    el: "#app-56",
    data: {
        items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
        nextNum: 10
    },
    methods: {
        randomIndex() {
            return Math.round(Math.random() * this.items.length);
        },
        add() {
            this.items.splice(this.randomIndex(), 0, this.nextNum++);
        },
        remove() {
            this.items.splice(this.randomIndex(), 1);
        }
    }
})
                        </pre>

                        <pre>
&lt;div id="app-56"&gt;
    &lt;button @click="add"&gt;Add&lt;/button&gt;

    &lt;button @click="remove"&gt;Remove&lt;/button&gt;

    &lt;transition-group name="list" tag="p"&gt;
        &lt;span v-for="item in items" :key="item"&gt;
            {{item}}
        &lt;/span&gt;
    &lt;/transition-group&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
.list-enter-active, .list-leave-active
{
    transition: all 0.5s ease;
}

.list-enter, .list-leave-to {
    transform: translateY(30px);
    opacity: 0;
}

#app-56 span {
    display: inline-block;
    margin-right: 20px;
}
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="app-56">
                                    <button @click="add">Add</button>
                                    <button @click="remove">Remove</button>

                                    <transition-group name="list"
                                                      tag="p">
                                        <span v-for="item in items"
                                              :key="item">
                                            {{item}}
                                        </span>
                                    </transition-group>
                                </div>
                            </samp>
                        </div>

                        <p>
                            需要注意span元素为inline level, 需要把它设置成block level, 否则transform无法生效
                        </p>

                        <p>
                            这里存在着问题, 当添加或移除元素时, 周围的元素会闪现到它们所在的新位置, 而不是平滑的过渡, 下面将解决这个问题.
                        </p>
                    </div>

                    <div>
                        <h4>列表的移动过渡</h4>

                        <p>
                            <code>transition-group</code>组件还有一个特殊之处, 不仅可以有进入和离开动画, 还可以改变定位. 这要使用新增的<code>v-move</code>类,
                            它会在元素改变定位的过程中应用. 就如之前的类名, 可以通过<code>name</code>attribute 自定义前缀, 也可以通过<code>move-class</code>
                            attribute手动设置.
                        </p>

                        <p>
                            另外, 还需要注意, Vue使用了一个叫<a href="https://aerotwist.com/blog/flip-your-animations/">FLIP</a>
                            的简单动画队列, 并通过<code>transform</code>属性将元素从当前位置平滑过渡到新的位置. 因此, 元素必须是块级的.
                        </p>

                        <p>
                            <code>v-move</code>对于设置过渡的切换时机和过渡曲线很有帮助, 例如 :
                        </p>

                        <pre>
let app57 = new Vue({
    el: "#app-57",
    data: {
        items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
        nextNum: 10
    },
    methods: {
        randomIndex() {
            return Math.round(Math.random() * this.items.length);
        },
        add() {
            this.items.splice(this.randomIndex(), 0, this.nextNum++);
        },
        remove() {
            this.items.splice(this.randomIndex(), 1);
        }
    }
})
                        </pre>

                        <pre>
&lt;div id="app-57"&gt;
    &lt;button @click="add"&gt;Add&lt;/button&gt;

    &lt;button @click="remove"&gt;Remove&lt;/button&gt;

    &lt;transition-group name="list-smooth" tag="p"&gt;
        &lt;span v-for="item in items" :key="item"&gt;
            {{item}}
        &lt;/span&gt;
    &lt;/transition-group&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
#app-57 span {
    display: inline-block;
    margin-right: 20px;
    /* 设置从正常文档流状态到其他状态的(进入, 离开, 移动)过渡 */
    transition: all 1s; 
}
.list-smooth-enter, .list-smooth-leave-to {
    transform: translateY(30px);
    opacity: 0;
}
.list-smooth-leave-active {
    /* 当元素处于正在离开的状态时, 要让删除的元素先脱离文档流, 旁边的元素才能移动过渡过来 */
    position: absolute;
}

/* 也可以手动设置从 move 到 move 结束的过渡/
/* .list-smooth-move {
    transition: all 1s;
} */
                        </pre>

                        <div id="app-57">
                            <button @click="add">Add</button>

                            <button @click="remove">Remove</button>

                            <transition-group name="list-smooth"
                                              tag="p">
                                <span v-for="item in items"
                                      :key="item">
                                    {{item}}
                                </span>
                            </transition-group>


                        </div>

                        <div class="bg-warning text-warning">
                            再次提醒, 注意使用FLIP过渡的元素不能设置为 <code>display: inline</code>. 作为替代, 可以设置display: inline-block
                            或放置于一个flex container中.
                        </div>
                    </div>

                    <div>
                        <h4>列表交错过渡</h4>
                        <p>
                            通过data attributes与js transitions通信, 可以做出交错过渡的效果.
                        </p>

                        <a
                           href="https://cn.vuejs.org/v2/guide/transitions.html#%E5%88%97%E8%A1%A8%E7%9A%84%E4%BA%A4%E9%94%99%E8%BF%87%E6%B8%A1">stragger
                            list example</a>

                        <!-- <div id="stragger-list-demo">
                            <input type="text"
                                   v-model="query">
                            <transition-group name="staggered-fade"
                                              tag="ul"
                                              :css="false"
                                              @before-enter="beforeEnter"
                                              @enter="enter"
                                              @leave="leave">

                                <li v-for="item, index in computedList"
                                    :key="item.msg"
                                    :data-index="index">
                                    {{item.msg}}
                                </li>
                            </transition-group>
                        </div> -->
                    </div>
                </article>

                <article>
                    <h2 id="317">可复用的过渡</h2>
                    <div>
                        <p>
                            过渡可以通过Vue的组件系统而被复用. 为了创建一个可复用的过渡, 我们只需要将<code>&lt;transition&gt;</code> 或
                            <code>&lt;transition-group&gt;</code> 作为模板的根根元素即可. 例如 :
                        </p>

                        <pre>
Vue.component('my-reusable-transition', {
    template: `
        &lt;transition
                name="xxx"
                mode="out-in"
                @before-enter="beforeEnter"
                @after-enter="afterEnter"&gt;
            &lt;slot&gt;&lt;/slot&gt;
        &lt;/transition&gt;
    `
})
                        </pre>

                        <p>
                            <a href="#436">函数式组件</a> 更适合这个任务 :
                        </p>

                        <pre>
Vue.component('my-special-transition', {
  functional: true,
  render: function (createElement, context) {
    var data = {
      props: {
        name: 'very-special-transition',
        mode: 'out-in'
      },
      on: {
        beforeEnter: function (el) {
          // ...
        },
        afterEnter: function (el) {
          // ...
        }
      }
    }
    return createElement('transition', data, context.children)
  }
})
                        </pre>
                    </div>
                </article>

                <article>
                    <h2 id="318">动态过渡</h2>
                    <div>
                        <p>
                            在Vue中过渡也是数据驱动的. 动态过渡的最基本例子就是通过 <code>name</code> attribute来绑定动态值. 例如 :
                        </p>
                        <pre>&lt;transition :name="transitionName"&gt; ... &lt;/transition&gt;</pre>

                        <p>
                            所有的过渡prop都是可以动态绑定的, 但我们不仅仅只有prop可用, 我们还可以通过事件钩子获取上下文中的所有数据,
                            因为事件钩子都是方法. 这就意味着, 根据组件的状态不同, js过渡会有不同的表现.
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="32">状态过渡</h1>

                <article>
                    <p>
                        Vue的过渡系统提供了许多简单的方法设置进入, 离开和列表的动画, 但是动画化数据本身呢 ? 例如 :
                    </p>
                    <ul>
                        <li>数字和计算</li>
                        <li>颜色展示</li>
                        <li>SVG节点的位置</li>
                        <li>元素的大小和其他属性</li>
                    </ul>
                    <p>
                        这些数据要么本身就以数值形式存储, 要么可以转换为数值. 有了这些, 就可以结合Vue的响应式和组件系统, 使用第三方库实现切换元素的过渡状态.
                    </p>
                </article>

                <article>
                    <h2 id="321">状态动画与侦听器</h2>
                    <div>
                        <p>
                            通过侦听器<code>watch</code>, 我们可以监听到任何数值更新.
                        </p>
                        <a
                           href="https://cn.vuejs.org/v2/guide/transitioning-state.html#%E7%8A%B6%E6%80%81%E5%8A%A8%E7%94%BB%E4%B8%8E%E4%BE%A6%E5%90%AC%E5%99%A8">example</a>
                    </div>
                </article>

                <article>
                    <h2 id="322">动态状态过渡</h2>
                    <div>

                    </div>
                </article>

                <article>
                    <h2 id="323">把过渡放到组件里</h2>
                    <div>
                        <a
                           href="https://cn.vuejs.org/v2/guide/transitioning-state.html#%E6%8A%8A%E8%BF%87%E6%B8%A1%E6%94%BE%E5%88%B0%E7%BB%84%E4%BB%B6%E9%87%8C">link</a>
                    </div>
                </article>

            </section>

            <section>
                <h1 id="41">混入(Mixin)</h1>

                <article>
                    <h2 id="411">基础</h2>
                    <div>
                        <p>
                            混入提供了一种灵活的方式, 来分发Vue组件中的可复用功能. 一个混入对象可以包含任意组件选项. 当组件使用混入对象时, 所有混入对象的选项将被"混合"进入组件本身的选项.
                        </p>

                        <pre>
// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"  
                        </pre>

                        <p>
                            <code>mixins</code>属于选项/组合, 类型为<code>Array&lt;Object&gt;</code>,
                            用于对组件进行局部混入.
                            该选项接收一混入对象的数组, 这些混入对象可以像正常的实例对象一样包含实例选项,
                            这些选项将会被合并到最终的选项中.
                        </p>

                        <p>
                            <code>Vue.extend</code>属于全局API, 其参数为<code>options</code>,
                            方法使用基础<code>Vue</code>构造器, 创建一个"子类". 注意 <code>data</code>选项是特例,
                            在<code>options</code>中必须是函数.
                        </p>

                        <p>
                            <code>extends</code>选项属于选项/组合, 类型为<code>Object | Function</code>,
                            在组件内部声明扩展另一个组件 (可以是一个简单的选项对象或构造函数),
                            而无需使用<code>Vue.extend</code>, 类似于<code>mixins</code>选项.
                            主要是为了便于扩展单文件组件, 例如 :
                        </p>

                        <pre>
let CompA = { // ...... }

// 在不调用 Vue.extend 的情况下继承 CompA
let CompB = {
    extends: CompA,
    // ......
}
                        </pre>

                        <p>
                            至此, 我们已经了解了全部的选项/组合 :
                        </p>
                        <ul>
                            <li>parent</li>
                            <li>extend</li>
                            <li>mixins</li>
                            <li>provide/inject</li>
                        </ul>
                    </div>
                </article>

                <article>
                    <h2 id="412">选项合并</h2>
                    <div>
                        <p>
                            当组件和混入对象有同名选项时, 这些选项将以恰当的方式进行"合并".
                        </p>

                        <p>
                            例如, data对象在内部会进行递归合并, 并在发生冲突时以组件数据优先.
                        </p>

                        <pre>
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})
                        </pre>

                        <p>
                            同名钩子函数会合并为一个数组, 它们都会被调用. 另外, 混入对象的钩子在组件自身钩子<strong>之前</strong>调用.
                        </p>

                        <pre>
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"
                        </pre>

                        <p>
                            值为对象的选项, 例如 <code>data</code>, <code>methods</code>,
                            <code>components</code>, 将被合并为一个对象.
                            两个对象property name冲突时, 取组件的属性值对.
                        </p>

                        <pre>
var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
                        </pre>

                        <p>
                            注意: <code>Vue.extend</code>使用同样的策略合并.
                        </p>


                    </div>
                </article>

                <article>
                    <h2 id="413">全局混入</h2>
                    <div>
                        <p>
                            混入也可以进行全局注册, 但是需要小心. 一旦使用全局混入, 它将影响每一个之后创建的Vue实例.
                            使用恰当时, 可以用来为自定义选项注入处理逻辑.
                        </p>
                        <pre>
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"
                        </pre>

                        <div class="bg-danger text-danger">
                            谨慎使用全局混入, 因为这会影响每个单独创建的Vue实例 (包括第三方组件). 推荐作为插件发布, 以避免重复应用混入.
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="414">自定义选项合并策略</h2>
                    <div>
                        <p>
                            自定义选项合并时使用默认策略即覆盖现有值. 如果想要一个自定义的合并策略,
                            需要添加一个函数到<code>Vue.config.optionMergeStrategies</code> :
                        </p>
                        <pre>
Vue.config.optionMergeStrategies.myOption = function (toValue, fromValue) {
    // return merged value
}
                        </pre>
                        <p>
                            对于多数值为对象的选项, 可以使用与<code>methods</code>相同的合并策略 :
                        </p>
                        <pre>
let strategies = Vue.config.optionMergeStrategies;
strategies.myOption = strategies.methods;
                        </pre>

                        <p>
                            <code>Vue.config.optionMergeStrategies</code>中定义了许多选项的合并策略, 类型为
                            <code>{ [key: string]: Function }</code>, key为选项名, 值为合并策略.
                        </p>

                        <p>
                            合并策略是一个函数, 其第一个参数为该选项的旧值, 第二个参数为选项的新值, Vue实例上下文被作为第三个参数传入.
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="42">自定义指令</h1>

                <article>
                    <h2 id="421">简介</h2>
                    <div>
                        <p>
                            Vue允许注册自定义指令. 尽管代码复用和抽象的主要形式是组件, 但有时仍需要对普通DOM元素进行底层操作, 这时就会用到自定义指令. 例如一个聚焦输入框, 当页面加载,
                            元素将获得焦点.
                        </p>

                        <pre>
// 注册一个全局自定义指令 'v-focus'
Vue.directive("focus", {
    inserted: function (el) {
        el.focus();
    }
})
                        </pre>

                        <p>
                            如果想注册局部指令, 组件中可接受一个<code>directives</code>选项 :
                        </p>

                        <pre>
directives: {
    focus: {
        // 指令定义
        inserted: function (el) {
            el.focus();
        }
    }
}
                        </pre>

                        <p>
                            这样, 该指令就只能在注册该指令的组件上使用, 然后在该组件的模板内的任意元素上使用自定义的指令.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="422">钩子函数</h2>
                    <div>
                        <p>
                            一个指令定义对象可以提供如下几个钩子函数 (均为可选) :
                        </p>
                        <ul>
                            <li>bind : 只调用一次, 指令第一次绑定到元素时调用. 在这里可以进行一次性的初始化设置</li>
                            <li>inserted : 被绑定元素插入父节点时调用 (仅保证父节点存在, 但不一定已被插入到文档中)</li>
                            <li>
                                update : 所在组件的VNode更新时调用, 但可能发生在其子VNode更新前.
                                指令的值可能发生了改变或没变. 但是可以通过比较更新前后的值来忽略不必要的模板更新
                            </li>
                            <li>componentUpdated : 指令所在组件的VNode及其子VNode全部更新后调用</li>
                            <li>unbind : 只调用一次, 指令与元素解绑时调用</li>
                        </ul>
                    </div>
                </article>

                <article>
                    <h2 id="423">钩子函数参数</h2>

                    <div>
                        <p>
                            指令钩子函数会被传入以下参数 :
                        </p>

                        <ul>
                            <li>el : 指令绑定的元素, 可用来操作DOM</li>
                            <li>binding : 一个对象, 包含以下属性 :
                                <ul>
                                    <li>name : 指令名, 不包括 <code>v-</code>前缀 </li>

                                    <li>value : 指令绑定值, 即指令表达式的值</li>
                                    <li>oldValue : 指令绑定的旧值, 仅在update和componentUpdated钩子中可用</li>

                                    <li>expression : 字符串形式的指令表达式.</li>
                                    <li>arg : 字符串, 代表传给指令的参数</li>
                                    <li>modifiers: 一个包含修饰符的对象, 其中属性名为修饰符名, 属性值为true</li>
                                </ul>
                            </li>
                            <li>vnode : Vue编译生成的虚拟节点</li>
                            <li>oldVNode : 旧虚拟节点, 仅在update 和 componentUpdated钩子中可用</li>
                        </ul>

                        <div class="bg-warning text-warning">
                            除了<code>el</code>外, 其他参数应是只读的, 切勿修改. 如果需要在钩子间共享数据, 建议使用元素的
                            <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/dataset">dataset</a></code>
                            来进行.
                        </div>

                        <br>

                        <p>
                            用例 :
                        </p>

                        <pre>
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    let s = JSON.stringify;

    el.innerHTML = 
      '&lt;strong>name:&lt;/strong> '        + s(binding.name) + 
      '&lt;br>&lt;br>' + 
      '&lt;strong>value&lt;/strong>: '       + s(binding.value) + 
      '&lt;br>&lt;br>' + 
      '&lt;strong>expression&lt;/strong>: '  + s(binding.expression) + 
      '&lt;br>&lt;br>' + 
      '&lt;strong>argument&lt;/strong>: '    + s(binding.arg) + 
      '&lt;br>&lt;br>' + 
      '&lt;strong>modifiers&lt;/strong>: '   + s(binding.modifiers) + 
      '&lt;br>&lt;br>' + 
      '&lt;strong>vnode keys&lt;/strong>: '  + Object.keys(vnode).join(', ')
  }
})

let hookArgDemo = new Vue({
  el: "#hook-arg-demo",
  data: {
      message: 'foo'
  }
});
                        </pre>

                        <pre>
&lt;div id="hook-arg-demo"
        v-demo:foo.a.b="message"&gt;
&lt;/div&gt;
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="hook-arg-demo"
                                     v-demo:foo.a.b="message"></div>
                            </samp>
                        </div>
                    </div>

                    <div>
                        <h4>动态指令参数</h4>

                        <p>
                            指令参数是可以动态的. 例如, 在<code>v-mydirective:[argument]="value"</code>中,
                            <code>argument</code>参数可以根据组件实例数据进行更新, 使得自定义指令更加灵活. 在钩子函数中, 可以通过函数的参数 ——
                            binding对象的arg属性解析出该指令动态参数的名称
                        </p>

                    </div>
                </article>

                <article>
                    <h2 id="424">函数简写</h2>
                    <div>
                        <p>
                            很多时候想让bind和update钩子触发相同行为, 而不关心其他钩子, Vue为此提供了简写,
                            只需要往<code>Vue.directive</code>的第二个参数传一个函数即可, 如 :
                        </p>

                        <pre>
Vue.directive('color-swatch', function(el, binding) {
    el.style.backgroundColor = binding.value
})
                        </pre>
                    </div>
                </article>

                <article>
                    <h2 id="425">对象字面量</h2>
                    <div>
                        <p>
                            如果指令需要多个值, 可以传入一个js字面量对象. 指令函数能够接受所有合法的js表达式.
                        </p>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="43">渲染函数&JSX</h1>

                <article>
                    <h2 id="431">基础</h2>
                    <div>
                        <p>
                            Vue推荐在绝大多数情况下使用模板来创建html. 在某些场景中, 需要js的完全编程能力. 这时可以用渲染寒素, 它比模板更接近编译器.
                        </p>

                        <p>
                            在下面的例子中, <code>render</code>函数很实用. 假设我们需要生成一些带锚点的标题 :
                        </p>

                        <pre>
&lt;h1&gt;
    &lt;a name="hello-world" href="#hello-world"&gt;
        Hello world!
    &lt;/a&gt;
&lt;/h1&gt;
                        </pre>

                        <p>
                            那么对于上面的html, 可能这样定义组件接口 :
                        </p>

                        <pre>
&lt;anchored-heading :level="1"&gt;Hello world!&lt;/anchored-heading&gt;
                        </pre>

                        <p>
                            当开始写一个只能通过<code>level</code> prop 动态生成标题(heading)的组件时, 可能会这样实现 :
                        </p>

                        <pre>
&lt;script type="text/x-template" id="anchored-heading-template"&gt;
    &lt;h1 v-if="level === 1"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h1&gt;

    &lt;h2 v-else-if="level === 2"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h2&gt;

    &lt;h3 v-else-if="level === 3"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h3&gt;

    &lt;h4 v-else-if="level === 4"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h4&gt;

    &lt;h5 v-else-if="level === 5"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h5&gt;

    &lt;h6 v-else-if="level === 6"&gt;
        &lt;slot&gt;&lt;/slot&gt;
    &lt;/h6&gt;
&lt;/script&gt;
                        </pre>

                        <pre>
Vue.component('anchored-heading-v1', {
    template: "#anchored-heading-template",
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})
                        </pre>

                        <p>
                            下面使用它进行测试 :
                        </p>

                        <script type="text/x-template"
                                id="anchored-heading-template">
                            <h1 v-if="level === 1">
                                <slot></slot>
                            </h1>
                        
                            <h2 v-else-if="level === 2">
                                <slot></slot>
                            </h2>
                        
                            <h3 v-else-if="level === 3">
                                <slot></slot>
                            </h3>
                        
                            <h4 v-else-if="level === 4">
                                <slot></slot>
                            </h4>
                        
                            <h5 v-else-if="level === 5">
                                <slot></slot>
                            </h5>
                        
                            <h6 v-else-if="level === 6">
                                <slot></slot>
                            </h6>
                        </script>

                        <pre>
&lt;anchored-heading-v1 id="anchored-heading-v1-demo"
                        :level="1"&gt;
    Anchored heading version 1
&lt;/anchored-heading-v1&gt;
                        </pre>

                        <pre>
let anchoredHeadingV1Demo = new Vue({
    el: "#anchored-heading-v1-demo"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <anchored-heading-v1 id="anchored-heading-v1-demo"
                                                     :level="1">
                                    Anchored heading version 1
                                </anchored-heading-v1>
                            </samp>
                        </div>

                        <p>
                            这里用模板并不是最好的选择: 不但代码冗长, 而且在每个标题中重复书写了slot, 在要插入锚定元素时还要重复
                        </p>

                        <p>
                            尝试用<code>render</code>函数重写上面模板 :
                        </p>

                        <pre>
Vue.component('anchored-heading-v2', {
    render: function (createElement) {
        return createElement(
            'h' + this.level, // 标签名
            this.$slots.default // 子节点数组
        )
    },
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})
                        </pre>

                        <p>
                            <code>&slots</code> 是Vue实例<code>vm</code>的一个property, 用来访问被插槽分发的内容. 每个具名插槽有其相应的property (例如
                            <code>v-slot:foo</code> 中的内容可以通过 <code>vm.$slots.foo</code> 访问). 其 <code>default</code>
                            property包括了所有没有被包含在具名插槽中的节点, 或<code>v-slot:default</code>的内容. 使用渲染函数会精简很多, 但是需要非常熟悉Vue的实例属性.
                            更多的实例属性参考—— <a
                               href="https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E5%B1%9E%E6%80%A7">link</a>
                        </p>

                        <p>
                            下面是使用例子 :
                        </p>

                        <pre>
&lt;anchored-heading-v2 id="anchored-heading-v2-demo"
                        :level="4"&gt;
    Anchored heading version 2
&lt;/anchored-heading-v2&gt;
                        </pre>

                        <pre>
let anchoredHeadingV1Demo = new Vue({
    el: "#anchored-heading-v2-demo"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <anchored-heading-v2 id="anchored-heading-v2-demo"
                                                     :level="4">
                                    Anchored heading version 2
                                </anchored-heading-v2>
                            </samp>
                        </div>
                    </div>
                </article>

                <article>
                    <h2 id="432">节点, 树以及虚拟DOM</h2>
                    <div>
                        <p>
                            浏览器在阅读html代码时会建立一个DOM节点树来保持追踪所有内容.
                        </p>

                        <p>
                            每个元素都是一个DOM节点, 每段文字也是一个节点, 甚至注释也是节点. 一个节点就是页面的一部分. 高效地更新所有这些节点会比较困难, 但不需要手动完成这个工作.
                            只需要告诉Vue我们希望页面上地HTML是什么, 这可以是一个<code>template</code>里, 或者一个渲染函数里 :
                        </p>

                        <pre>
&lt;h1&gt;{{ blogTitle }}&lt;/h1&gt;
                        </pre>

                        <pre>
render: function (createElement) {
    return createElement('h1', this.blogTitle)
}
                        </pre>

                        <p>
                            这两种情况下, Vue都会自动保持页面更新, 即便<code>blogTitle</code>发生变化.
                        </p>
                    </div>

                    <div>
                        <h4>虚拟DOM</h4>
                        <p>
                            Vue通过建立一个<strong>虚拟DOM</strong>来追踪自己如何改变真实DOM. 对于下面一行代码 :
                        </p>

                        <pre>
return createElement('h1', this.blogTitle)
                        </pre>

                        <p>
                            这里<code>createElement</code>返回值不是一个实际的DOM元素. 这个方法更准确的名字可能是
                            <code>createNodeDescription</code>, 因为返回值包含的信息告诉Vue页面上需要渲染什么样的节点, 包括其子节点的描述信息. 这样的节点可描述为
                            "虚拟节点(virtual node)", 简称为"VNode". "虚拟DOM(virtual DOM)" 是我们对 由Vue组件树建立起来的整个VNODE树的称呼.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="433">createElement参数</h2>
                    <div>
                        <p>
                            接下来需要熟悉如何在<code>createElement</code>函数中使用模板中的哪些功能. 这是<code>createElement</code>接受的参数 :
                        </p>

                        <pre>
// @return {VNode} 返回VNode
createElement(
    // 第一个参数为 String | Object | Function 类型
    // 是一个标签名(标签名既可以是普通元素也可以是已注册组件名), 
    // 组件的option对象, 
    // 或者一个解析 (resolve)了前面所提的值的一个 async函数.
    // 必选项
    'div',

    // 第二个参数为 Object类型
    // 一个与模板中属性对应的数据对象, 详情见下节
    // 可选项
    {},

    // 第三个参数为一个 String | Array&lt;VNode | String&gt;类型
    // 子级VNodes, 由 createElement() 构建而成
    // 也可以用字符串来生成文本虚拟节点. 

    [
        'Hello',
        createElement('h1', 'foo'),
        createElement(MyComponent, {
            props: {
                bar: 'baz'
            }
        }),
        // ......
    ]
)
                        </pre>

                        <p>
                            虽然js没有函数重载, 但估计createElement对参数进行了类型判断, 可以让我们使用2个参数的形式.
                            第二个可以为数据对象, 也可以为子级VNodes.
                        </p>
                    </div>

                    <div>
                        <h4>深入数据对象</h4>
                        <p>
                            需要注意: 正如 <code>v-bind:class</code> 和 <code>v-bind:style</code> 在模板语法中会被特别处理(合并策略),
                            它们在VNode数据对象中也有对应的顶层字段(top-level fields). 该对象也允许绑定普通的HTML attribute, 也允许绑定例如
                            <code>innerHTML</code>这样的DOM property (这样会覆盖 <code>v-html</code> 指令, 同时也会覆盖掉子VNodes, 即第三个参数)
                        </p>

                        <pre>
{
    // 与 v-bind:class 的 api相同
    // 接受一个字符串, 对象或字符串和对象组成的数组
    // 这里class 需要引号括起来是因为 class 是js关键字
    'class' : {
        foo: true,
        bar: false
    },

    // 与 v-bind:style 的 api相同
    // 接受一个字符串, 对象或字符串和对象组成的数组
    style: {
        color: 'red',
        fontSize: '14px'
    },

    // 普通的HTML attribute
    attrs: {
        id: 'foo'
    },

    // 组件 prop
    props: {
        myProp: 'bar'
    }

    // DOM property
    domProps: {
        innnerHTML: 'baz'
    }

    // 自定义事件的监听器在 on property内,
    // 但不再支持 如 v-on:keyup.enter 中 .enter这样的修饰器
    // 需要在处理函数中手动检查 keyCode
    on: {
        click: this.clickHandler
    },

    // 仅用于组件, 用于监听原生事件, 而不是组件内部使用 vm.$emit 触发的事件
    nativeOn: {
        click: this.nativeClickHandler
    },

    // 自定义指令. 注意, 无法对 binding钩子中的oldValue 赋值, 因为 Vue已自动同步
    directives: [
        {
            name: 'my-custom-directive',
            value: '2',
            expression: '1 + 1',
            arg: 'foo',
            modifiers: {
                bar: true
            }
        }
    ],

    // 作用域插槽, 格式为 : 
    // {slotName: slotProps => string | VNode | Array&lt;VNode&gt;}
    scopedSlots: {
        default: props => createElement('span', props.text)
    },

    // 如果组件是其他组件的子组件, 需要为插槽指定名称
    slot: 'name-of-slot',

    // 其他特殊顶层属性
    key: 'myKey',
    ref: 'myRef',

    // 如果在渲染函数中给多个元素都应用了相同的 ref 名,
    // 那么 $refs.myRef 就会变成一个数组
    refInFor: true
}
                        </pre>
                    </div>

                    <div>
                        <h4>完整示例</h4>

                        <p>
                            现在我们就可以实现最开始的组件
                        </p>

                        <pre>
let getChildrenTextContent = function (children) {
    return children.map(function (node) {
        return node.children ? 
               getChildrenTextContent(node.children) : 
               node.text
    }).join('');
}

Vue.component('anchored-heading', {
    render: function (createNodeDescription) {
        // 创建 kebab-case 风格id
        let headingId = getChildrenTextContent(this.$slots.default)
            .toLowerCase()
            .replace(/\W+/g, '-') // 将非word替换为-
            .replace(/(^-|-$)/g, '') // 将开头的-和结尾的- 去掉

        return createNodeDescription(
            'h' + this.level,
            [
                createNodeDescription('a', {
                    attrs: {
                        name: headingId,
                        href: '#' + headingId
                    }
                }, this.$slots.default)
            ]
        )
    },
    props: {
        level: {
            type: Number,
            required: true
        }
    }
})
                        </pre>

                        <p>
                            下面使用它进行测试 :
                        </p>

                        <pre>
&lt;anchored-heading id="anchored-heading-demo"
                     :level="1"&gt;
    &lt;!-- 在插槽那章讲到, 如果组件模板中没有给出slot --&gt;
    &lt;!-- 则分发内容会被丢弃. --&gt;
    &lt;!-- 但其实它们仍然会被存储在组件的$slot.default中 --&gt;
    &lt;!-- 这样渲染函数就可以利用它来进行渲染 --&gt;
    Anchored heading
&lt;/anchored-heading&gt;
                        </pre>

                        <pre>
let anchoredHeadingDemo = new Vue({
    el: "#anchored-heading-demo"
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <anchored-heading id="anchored-heading-demo"
                                                  :level="1">
                                    Anchored heading
                                </anchored-heading>
                            </samp>
                        </div>

                        <p>
                            注意, 渲染函数内<code>this</code>指向本组件.
                        </p>
                    </div>

                    <div>
                        <h4>约束</h4>
                        <p>
                            <strong>VNode必须唯一</strong>
                        </p>

                        <p>
                            组件树中的所有VNode必须是唯一的. 这意味着, 下面的渲染函数是不合法的 :

                        </p>

                        <pre>
render: function (createElement) {
    let myParagraphVNode = createElement('p', 'hi');

    return createElement('div', [
        myParagraphVNode, myParagraphVNode
    ])
}
                        </pre>

                        <p>
                            如果真的需要重复很多次的元素/组件, 可以使用工厂函数来实现. 例如, 下面的渲染函数用合法方式渲染了20个相同段落 :
                        </p>

                        <pre>
render: function (createElement) {
    return createElement('div', 
        Array.apply(null, {length: 20}).map(function () {
            // 创建一个长度为20的数组, 将每一项map成 createElement('p', 'hi'), 
            // 这样每一项的VNode都是唯一的
            return createElement('p', 'hi');
        })
    )
}
                        </pre>

                        <p>
                            这里调用<code>Array.apply(null, { length: 20 })</code>
                            是因为这样才能创建出一个有20个<code>undefined</code>值的数组.
                            如果直接调用<code>Array(20)</code>将会创建出一个长度为20的空数组.
                            在控制台会看见这样创建的数组会被打印为<code>[empty x 20]</code>.
                            虽然两种方式在打印数组项的时候都会打印出<code>undefined</code>,
                            但是前一种方法打印出<code>undefined</code>是因为项值确实为<code>undefined</code>,
                            而后者打印出<code>undefined</code>是因为项为空, 两者本质是不同的.
                            而<code>map</code>方法不会把一个空项map成我们想要的结果, 只是保留该空项.
                            因此我们应该使用前一种方式.
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="434">使用js代替模板功能</h2>

                    <div>
                        <h4>v-if 和 v-for</h4>
                        <p>
                            对于js中可以轻松完成的操作, Vue的渲染函数不会提供专有的替代方法. 例如 v-if 和 v-for :
                        </p>

                        <pre>
&lt;div id="templateVersionList"&gt;
    &lt;ul v-if="items.length"&gt;
        &lt;li v-for="item in items"&gt;
            {{ item.name }}
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;p v-else&gt;No items left&lt;/p&gt;
&lt;/div&gt;
                        </pre>

                        <pre>
let templateIfFor = new Vue({
    el: "#templateVersionList",
    data: {
        items: [0, 1, 2, 3]
    }
})
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="templateVersionList">
                                    <ul v-if="items.length">
                                        <li v-for="item in items">
                                            {{ item }}
                                        </li>
                                    </ul>
                                    <p v-else>No items left</p>
                                </div>
                            </samp>
                        </div>

                        <p>
                            以上可用在渲染函数中用js 的 if / else 和 map重写 :
                        </p>

                        <pre>
Vue.component("renderVersionList", {
    props: {
        items: {
            type: Array,
            required: true
        }
    },
    render: function (createElement) {
        if (this.items.length) {
            return createElement('ul', 
                this.items.map( function (item) {
                    return createElement('li', item);
                })
            )
        }
        else {
            return createElement('p', 'No items left');
        }
    }
}) 
                        </pre>
                        <pre>
&lt;div id="renderVersionList"&gt;&lt;/div&gt;
                        </pre>
                        <pre>
let templateVersionList = new Vue({
    el: "#templateVersionList",
    data: {
        items: [0, 1, 2, 3]
    },
    template: `
        &lt;render-version-list :item="items"&gt;&lt;/render-version-list&gt;
    `
});
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="renderVersionList">
                                </div>
                            </samp>
                        </div>

                    </div>

                    <div>
                        <h4>v-model</h4>
                        <p>
                            渲染函数中没有与<code>v-model</code>的直接对应 —— 必须自己实现相应逻辑,
                            利用数据选项中的<code>domProps</code>, <code>on</code> 等属性来实现. 例如 :
                        </p>

                        <pre>
Vue.component("render-input", {
  props: ["value"],
  render: function (createElement) {
    let self = this;
    return createElement("input", {
      domProps: {
        value: this.value,
      },
      on: {
        input: function (event) {
          // 现在this在处理函数中, 上下文发生变化, 
          // 必须通过定义好的self才能使用组件实例的&emit方法
          self.$emit("input", event.target.value);
        }
      }
    });
  }
});

let renderInputDemo = new Vue({
    el: "#render-input-demo",
    data: {
        msg: ""
    }
})
                        </pre>

                        <pre>
&lt;div id="render-input-demo"&gt;
    &lt;render-input v-model="msg"&gt;
    &lt;/render-input&gt;
    {{ msg }}
&lt;/div&gt;
                        </pre>


                        <div class="code-output">
                            <samp>
                                <div id="render-input-demo">
                                    <render-input v-model="msg">
                                    </render-input>
                                    {{ msg }}
                                </div>
                            </samp>
                        </div>
                    </div>

                    <div>
                        <h4>事件&按键修饰符</h4>

                        <p>
                            对于<code>.passive</code>, <code>.capture</code> 和 <code>.once</code>
                            这些事件修饰符, Vue提供了相应的前缀可用于数据对象中<code>on</code>的property上 :
                        </p>

                        <table style="max-width: 500px;"
                               class="table table-striped table-bordered table-hover">
                            <thead>
                                <th>修饰符</th>
                                <th>前缀</th>
                            </thead>

                            <tbody>
                                <tr>
                                    <td>.passive</td>
                                    <td>&</td>
                                </tr>
                                <tr>
                                    <td>.capture</td>
                                    <td>!</td>
                                </tr>
                                <tr>
                                    <td>.once</td>
                                    <td>~</td>
                                </tr>
                                <tr>
                                    <td>.capture.once 或 .once.capture</td>
                                    <td>~!</td>
                                </tr>
                            </tbody>

                        </table>

                        <p>
                            例如 :
                        </p>

                        <pre>
on: {
    '!click': this.onclick,
    '~keyup': this.onKeyupOnce
}
                        </pre>

                        <p>
                            对于所有其他修饰符, 私有前缀不是必须的, 因为可以在事件处理函数中使用事件方法
                        </p>

                        <table class="table table-striped table-bordered table-hover"
                               style="max-width: 500px">
                            <thead>
                                <th>修饰符</th>
                                <th>处理函数中的等价操作</th>
                            </thead>

                            <tbody>
                                <tr>
                                    <td>.stop</td>
                                    <td>event.stopPropagation</td>
                                </tr>
                                <tr>
                                    <td>.prevent</td>
                                    <td>event.preventDefault</td>
                                </tr>
                                <tr>
                                    <td>.self</td>
                                    <td>if (event.target !== event.currentTarget) return</td>
                                </tr>
                                <tr>
                                    <td>按键: .enter, .13</td>
                                    <td>if (event.keyCode !== 13) return (对于别的按键修饰符同理)</td>
                                </tr>
                                <tr>
                                    <td>修饰键: .ctrl, .alt, .shift, .meta</td>
                                    <td>if (!event.ctrlKey) return (其余同理)</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>

                    <div>
                        <h4>插槽</h4>

                        <p>
                            可以通过<code>this.$slots</code>访问静态插槽(相对于作用域插槽)的内容, 
                            每个插槽都是一个VNode数组 : 
                        </p>

                        <pre>
Vue.component('render-slot', {
  render: function (createElement) {
    return createElement(
      // 等价模板为 : &lt;p&gt;&lt;slot name="foo"&gt;&lt;/slot&gt;&lt;/p&gt;
      'p',
      this.$slots.foo
    )
  }
});

let renderSlotDemo = new Vue({
  el: "#render-slot-demo"
});
                        </pre>

                        <pre>
&lt;render-slot id="render-slot-demo" #foo&gt;
    hello vue
&lt;/render-slot&gt;
                        </pre>

                        <div class="code-output">
                            <samp>
                                <render-slot id="render-slot-demo" #foo>
                                    hello vue
                                </render-slot>
                            </samp>
                        </div>

                        <p>
                            也可以通过 <code>this.$scopedSlots</code>访问作用域插槽, 每个作用域插槽都是一个返回若干VNode的函数 : 
                        </p>

                        <pre>
Vue.component("render-scopedslot", {
  props: ["propA", "propB"],
  render: function (createElement) {
    // 等价模板为 : 
    // &lt;p&gt;
    //   &lt;slot name="foo" :slotPropA="propA" :slotPropB="propB"&gt;
    //   &lt;/slot&gt;
    // &lt;/p&gt;
    return createElement(
      "p",
      this.$scopedSlots.foo({
        slotPropA: this.propA,
        slotPropB: this.propB,
      })
    );
  },
});

let renderScopedSlotDemo = new Vue({
  el: "#render-scopedslot-demo"
})
                        </pre>

                        <pre>
&lt;div id="render-scopedslot-demo"&gt;
    &lt;render-scopedslot prop-a="prop a" 
                       prop-b="prop b"
                       #foo="{slotPropA: A, slotPropB: B"&gt;
        {{ A }}, {{ B }}
    &lt;/render-scopedslot&gt;
&lt;/div&gt;
                        </pre>

                        <div class="code-output">
                            <samp>
                                <div id="render-scopedslot-demo">
                                    <render-scopedslot prop-a="prop a"
                                          prop-b="prop b"
                                          #foo="{slotPropA: A, slotPropB: B}">
                                          {{ A }}, {{ B }}
                                    </render-scopedslot>
                                </div>
                            </samp>
                        </div>

                        <p>
                            上面两个例子展示了如何通过渲染函数渲染插槽元素, 
                            下面展示了如何通过渲染函数向子组件的插槽分发内容.
                        </p>

                        <p>
                            如果要用渲染函数向子组件中传递作用域插槽, 可利用 VNode数据对象中的 <code>scopedSlots</code> 字段 : 
                        </p>

                        <pre>
render: function (createElement) {
    return createElement(
        // 等价的模板为 : 
        // &lt;div&gt;
        //   &lt;child v-slot:default="props"&gt;
        //     &lt;span&gt; {{ props.text }}&gt;/span&gt;
        //   &lt;/child&gt;
        // &lt;/div&gt;
        'div',
        [
            createElement(
                'child',
                {
                    scopedSlots: {
                        default: function (props) {
                            return createElement('span', props.text)
                        }
                    }
                }
            )
        ]
    )
}
                        </pre>

                        <p>
                            也可以利用这个方式给子组件的静态slot分发内容, 只不过此时该slot没有slot props. 
                        </p>

                        <p>
                            还可以利用数据对象中的<code>slot</code>字段指定该子组件作为父组件的哪个插槽内容 : 
                        </p>

                        <pre>
&lt;div id="app">
&lt;/div>

&lt;script>
    Vue.component("Child", {
        template: `
            &lt;div>&lt;slot>&lt;/slot>&lt;/div>
        `
    });

    Vue.component("Parent", {
        template: `
            &lt;div>&lt;slot name="foo">&lt;/slot>&lt;/div>
        `
    });

    new Vue({
        render: h => h("Parent", [
            h("Child", { slot: "foo" }, "default")
        ])
    }).$mount("#app");
&lt;/script>
                        </pre>

                        <p>
                            模板中独占插槽的缩写语法的等价实现只能通过<code>scopedSlots</code>字段, <code>slot</code>字段不可用. 
                        </p>
                        
                    </div>
                </article>

                <article>
                    <h2 id="435">JSX</h2>
                    <div>

                    </div>
                </article>

                <article>
                    <h2 id="436">函数式组件</h2>
                    <div>
                        <p>
                            之前的锚点标题组件是比较简单的, 没有管理任何状态, 
                            也没有监听任何传递给它的状态, 也没有生命周期方法. 
                            实际上, 它只是一个接受一些prop的函数. 
                            这种场景下, 我们可以将组件标记为<code>functional</code>, 
                            意味着它无状态 (没有响应式数据), 
                            也没有实例(没有<code>this</code> 上下文). 
                            一个<strong>函数式组件</strong>就像这样 :
                        </p>
                        <pre>
Vue.component('my-component', {
    functional: true,
    // props可选
    props: {
        // ...
    }

    // 为了弥补缺少的实例
    // 提供第二个参数作为上下文
    render: function (createElement, context) {
        // ...
    }
})
                        </pre>

                        <div class="alert-info">
                            2.3.0之前版本中, 一个函数式组件想要接受prop, 
                            则<code>props</code>选项是必须的. 
                            在2.3.0或以上的版本中, 可以省略<code>props</code>选项, 
                            所有组件上的attribute都会被自动隐式解析为prop.

                            <br>

                            当使用函数式组件时, 引用(???)将会是<code>HTMLElement</code>, 
                            因为函数式组件无状态无实例.
                        </div>

                        <br>

                        <p>
                            在2.5.0以上版本中, 如果使用了单文件组件, 那么基于模板的函数式组件可以这样声明 : 
                        </p>

                        <pre>
&lt;template functional&gt;
&lt;/template&gt;
                        </pre>

                        <p>
                            函数式组件拥有以下特征 : 
                        </p>
                        
                        <ul>
                            <li>没有data, computed, watcher, 生命周期函数, 和 methods等选项</li>
                            <li>没有template选项, 除非它通过单文件组件进行预编译, 但可以有render函数</li>
                            <li>可以传递prop, attribute, event, slot...</li>
                            <li>返回一个VNode</li>
                        </ul>

                        <p>
                            组件需要的一切通过<code>context</code>提供, 
                            它是一个包括以下属性的对象 : 
                        </p>

                        <ul>
                            <li>props : 提供所有prop的对象</li>
                            <li>children : VNode子节点数组</li>
                            <li>slots : 一个函数, 返回了包含所有插槽的对象</li>
                            <li>scopedSlots(2.6.0+) : 一个暴露传入的作用域插槽的对象. 也以函数形式暴露普通插槽</li>
                            <li>data : 传递给组件的整个数据对象, 作为createElement的第二个参数传入组件</li>
                            <li>parent : 对父组件的引用</li>
                            <li>listeners (2.3.0+) : 一个包含了所有父组件为当前组件注册的事件监听器的多项. 这是 data.on 的一个别名</li>
                            <li>injections (2.3.0+) : 如果使用了inject选项, 则该对象包含了应当被注入的属性</li>
                        </ul>

                        <p>
                            注意到<code>context</code>作为渲染函数第二个参数, 
                            这意味着我们还可以使用解构语法将<code>context</code>对象解构.
                        </p>

                        <p>
                            在设置 <code>functional</code>选项为<code>true</code>后, 我们需要更新我们的锚定标题组件的渲染函数,
                            为其增加 <code>context</code>参数, 并把<code>this.$slots.default</code>更新为<code>context.children</code>, 
                            然后将<code>this.level</code> 更新为 <code>context.props.level</code>.
                        </p>

                        <p>
                            因为函数式组件只是函数, 所以渲染开销会低很多.
                        </p>

                        <p>
                            在作为包装组件时它们同样非常有用. 例如, 当我们需要做这些时 : 
                        </p>

                        <ul>
                            <li>程序化地在多个组件中选择一个来渲染</ol>
                            <li>将 <code>children</code>, <code>props</code>, <code>data</code> 传递给子组件之前操作他们</ol>
                        </ul>
                    </div>

                    <div>
                        <h4>向子元素或组件传递attribute和事件</h4>

                        <p>
                            在普通组件中, 非prop的attribute会自动添加到组件的根元素上 ( 如果没有指定<code>inheritAttrs： false</code> ), 
                            将已有的同名attribute进行替换或智能合并. 
                        </p>

                        <p>
                            然而函数式组件不能这样 : 
                        </p>

                        <pre>
Vue.component('base-input', {
    functional: true,
    render: function (createElement) {
        return createElement(
            'input',
            // 无法自动将非prop的attribute(本例为placeholder)添加到根元素
        )
    }
})

let demo = new Vue({
    el: "#demo",
    template: `
        &lt;base-input placeholder="bar"&gt;
        &lt;/base-input&gt;
    `
})
                        </pre>
                        
                        <pre>
&lt;div id="demo"&gt;&lt;/demo&gt;
                        </pre>

                        <p>
                            我们应该显式地通过<code>context</code>完成这项工作 : 
                        </p>

                        <pre>
Vue.component('base-input', {
    functional: true,
    render: function (createElement, context) {
        // 透明地传递任何attributes, event listeners, children 等
        return createElement(
            'input', 
            context.data,
            context.children
        )
    }
})
                        </pre>
                    </div>
                </article>

                <article>
                    <h2 id="437">模板编译</h2>
                    <div>

                    </div>
                </article>
            </section>

            <section>
                <h1 id="44">插件</h1>

                <article>
                    <p>
                        插件通常用来为Vue添加全局功能. 插件的功能范围没有严格限制 —— 一般有下面几种 :
                    </p>
                    <ol>
                        <li>
                            添加全局方法或属性. 如 : <a
                               href="https://github.com/karol-f/vue-custom-element">vue-custom-element</a>
                        </li>
                        <li>
                            添加全局资源 : 指令/过滤器/过渡等. 如 <a href="https://github.com/vuejs/vue-touch">vue-touch</a>
                        </li>
                        <li>
                            通过全局混入添加一些组件选项. 如<a href="https://github.com/vuejs/vue-router">vue-router</a>
                        </li>
                        <li>添加Vue实例方法, 通过把这些方法添加到<code>Vue.prototype</code></li>
                        <li>
                            一个库, 提供自己的api, 同时提供上面提到的功能. 如<a href="https://github.com/vuejs/vue-router">vue-router</a>
                        </li>
                    </ol>
                </article>

                <article>
                    <h2 id="441">使用插件</h2>
                    <div>
                        <p>
                            通过全局方法<code>Vue.use</code>使用插件.
                            <code>Vue.use</code>属于全局API, 其第一个参数为 <code>{Object | Function} plugin</code>.
                            如果插件是个对象, 则该对象必须提供<code>install</code>方法,
                            如果是函数, 则该函数被视为install方法.
                            第二个参数为一个选项对象(可选).
                            install方法调用时, 会将<code>Vue</code>作为参数传入.
                            该方法需要在调用<code>new Vue()</code>启动应用前完成 :
                        </p>

                        <pre>
// 或调用插件的install方法 : MyPlugin.install(Vue)
Vue.use(MyPlugin);

new Vue({
    // ...
})
                        </pre>

                        <p>
                            <code>Vue.use</code>自动阻止多次注册相同插件, 届时即使多次调用也只会注册一次该插件.
                        </p>

                        <p>
                            Vue.js官方提供的一些插件(如 <code>vue-router</code>
                            )在检测到<code>Vue</code>是可访问的全局变量时会自动调用<code>Vue.use</code>进行注册.
                            在其他环境下应该始终显式调用<code>Vue.use</code>
                        </p>
                    </div>
                </article>

                <article>
                    <h2 id="442">开发插件</h2>
                    <div>
                        <p>
                            Vue.js的插件为对象时应该暴露一个<code>install</code>方法. 方法的第一参数为Vue构造器, 第二参数为可选的选项对象. 例如 :
                        </p>

                        <pre>
MyPlugin.install = function (Vue, options) {
    // 1. 添加全局方法或属性
    Vue.myGlobalMethod = function () {}
    
    Vue.myGlobalProperty = ...;

    // 2. 添加全局资源
    Vue.directive('my-directive', {
        bind (el, binding, vnode, oldVnode) {
            // ...
        }
    })

    // 3. 混入组件选项
    Vue.mixin({
        created: function () {}
    })

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function (methodOptions) {
        // ...
    }
}
                        </pre>
                    </div>
                </article>
            </section>

            <section>
                <h1 id="45">过滤器</h1>

                <article>
                    <p>
                        Vue.js允许自定义过滤器, 用于一些常见文本格式化. 过滤器可以用在两个地方 : 1.双花括号插值; 2. <code>v-bind</code>表达式(2.1.0+起).
                    </p>

                    <p>
                        过滤器是特殊的函数, 可以通过组件选项<code>filters</code>定义一个只作用在该组件上的局部过滤器,
                        该选项属于选项/资源, 类型为对象, 其键为定义的过滤器名, 值为一个函数.
                        过滤器通过运算符 <code>|</code> 进行调用, 运算符左边为操作数, 右边为过滤器名,
                        运算符左边的操作数会隐式地作为过滤器的第一个参数被传入. 例如 :
                    </p>

                    <pre>
let filterDemo = new Vue({
    el: "#filter-demo",
    data: {
        msg: ""
    },
    filters: {
        capitalize: function (value) {
            if (!value) return ''
            value = value.toString();
            return value.charAt(0).toUpperCase() + value.slice(1);
        }
    }
})
                    </pre>

                    <pre>
&lt;div id="filter-demo"&gt;
    &lt;input type="text" v-model="msg"&gt;
        {{msg | capitalize}}
&lt;/div&gt;
                    </pre>

                    <div class="code-output">
                        <samp>
                            <div id="filter-demo">
                                <input type="text"
                                       v-model="msg">
                                {{msg | capitalize}}
                            </div>
                        </samp>
                    </div>

                    <p>
                        或者在创建Vue实例之前全局定义过滤器 :
                    </p>

                    <pre>
Vue.filter('capitalize', function (value) {
    //......
})

new Vue({
    // ......
})
                    </pre>

                    <p>
                        当全局过滤器和局部过滤器重名时会采用局部过滤器
                    </p>

                    <p>
                        过滤器也可以串联, 将前一个过滤器过滤的结果作为左边的操作数
                    </p>

                    <pre>
{{msg | filterA | filterB}}
                    </pre>

                    <p>
                        如果需要给过滤器提供其他参数, 则需要显式的通过
                        运算符号<code>()</code>调用该过滤器并把其他参数传递进去.
                    </p>

                    <pre>
{{ msg | filterA('arg1', arg2) }}
                    </pre>

                    <p>
                        需要注意, <code>|</code>左边的操作数仍会隐式地作为第一个参数传入
                    </p>

                    <br>

                    <p>
                        至此, 我们已经了解了所有的选项/资源 :
                    </p>
                    <ul>
                        <li>components</li>
                        <li>directives</li>
                        <li>filters</li>
                    </ul>

                    <p>
                        最后, 不知道为啥文档里没有提到一个叫<code>Vue.options</code>的全局API, 它是一个对象,
                        其中有属性 <code>components</code>, <code>directives</code>, <code>filters</code> 和
                        <code>_base</code>, 应该这边涉及到了源码. 总之现在能了解到的是 : <br>
                        通过全局注册的组件, 自定义指令, 过滤器, 会被放在这三个对象中.
                    </p>
                </article>
            </section>
        </div>
    </div>

    <script src="./script/script.js"></script>
</body>

</html>